課程目錄

毫無(wú)疑問(wèn),Python如今是野蠻增長(zhǎng),增速非常快。隨著人工智能的崛起,Python會(huì)更加的火爆。比爾蓋茨說(shuō)過(guò),之后的幾十年,人工智能會(huì)強(qiáng)大到足以引起人們的重視。Python將有可能沖擊每月編程語(yǔ)言排行榜第一的位置。

Python可以說(shuō)是一門比較功利的語(yǔ)言,它不復(fù)雜,讀和寫都非常易于使用,所以才有了“人生苦短,我用Python”這樣的調(diào)侃。

這套教程非常全面而且詳細(xì),從Python入門到Python進(jìn)階、Django、Flask等Web框架以及爬蟲(chóng)、數(shù)據(jù)庫(kù)、算法與數(shù)據(jù)結(jié)構(gòu)等方面均有涉及,幾乎覆蓋了Python 基礎(chǔ)及進(jìn)階的方方面面。

0引言

微信公眾號(hào)終于可以插代碼了,Python 可以走一波了。首先我承認(rèn)不是硬核搞 IT 的,太高級(jí)的玩法也玩不來(lái),講講下面基本的還可以,之后帶點(diǎn)機(jī)器學(xué)習(xí)、金融工程和量化投資的實(shí)例也是可以。

Python 入門篇 (上)

Python 入門篇 (下)

數(shù)組計(jì)算之 NumPy

科學(xué)計(jì)算之 SciPy

數(shù)據(jù)結(jié)構(gòu)之 Pandas

基本可視化之 Matplotlib

統(tǒng)計(jì)可視化之 Seaborn

交互可視化之 Bokeh

炫酷可視化之 PyEcharts

機(jī)器學(xué)習(xí)之 Sklearn

深度學(xué)習(xí)之 TensorFlow

深度學(xué)習(xí)之 Keras

深度學(xué)習(xí)之 PyTorch

深度學(xué)習(xí)之 MXnet

整個(gè)系列力求精簡(jiǎn)和實(shí)用 (可能不會(huì)完整,但看完此貼舉一反三也不要完整,追求完整的建議去看書(shū)),到了「難點(diǎn)處」我一定會(huì)畫(huà)圖幫助讀者理解。Python 系列的入門篇的目錄如下,本帖是上篇,只涵蓋前三個(gè)節(jié),下篇接著后兩節(jié)。

對(duì)于任何一種計(jì)算機(jī)語(yǔ)言,我覺(jué)得最重要的就是「數(shù)據(jù)類型」「條件語(yǔ)句 & 迭代循環(huán)」和「函數(shù)」,這三方面一定要打牢基礎(chǔ)。此外 Python 非常簡(jiǎn)潔,一行代碼 (one-liner) 就能做很多事情,很多時(shí)候都用了各種「解析式」,比如列表、字典和集合解析式。

在學(xué)習(xí)本貼前感受一下這個(gè)問(wèn)題:如何把以下這個(gè)不規(guī)則的列表 a 里的所有元素一個(gè)個(gè)寫好,專業(yè)術(shù)語(yǔ)叫打平 (flatten)?

a = [1, 2, [3, 4], [[5, 6], [7, 8]]]

魔法來(lái)了 (這一行代碼有些長(zhǎng),用手機(jī)的建議橫屏看)

fn = lambda x: [y for l in x for y in fn(l)] if type(x) is list else [x]

fn(a) 

[1, 2, 3, 4, 5, 6, 7, 8]

這一行代碼,用到了迭代、匿名函數(shù)、遞推函數(shù)、解析式這些技巧。初學(xué)者一看只會(huì)說(shuō)“好酷啊,但看不懂”,看完本帖和下帖后,我保證你會(huì)說(shuō)“我也會(huì)這樣用了,真酷!”

1基本數(shù)據(jù)類型

Python 里面有自己的內(nèi)置數(shù)據(jù)類型 (build-in data type),本節(jié)介紹基本的三種,分別是整型 (int),浮點(diǎn)型 (float),和布爾型 (bool)。

1.1

整型

整數(shù) (integer) 是最簡(jiǎn)單的數(shù)據(jù)類型,和下面浮點(diǎn)數(shù)的區(qū)別就是前者小數(shù)點(diǎn)后沒(méi)有值,后者小數(shù)點(diǎn)后有值。例子如下:

a = 1031

print( a, type(a) )

1031

通過(guò) print 的可看出 a 的值,以及類 (class) 是 int。Python 里面萬(wàn)物皆對(duì)象(object),「整數(shù)」也不例外,只要是對(duì)象,就有相應(yīng)的屬性 (attributes) 和方法 (methods)。

知識(shí)點(diǎn)

通過(guò) dir( X ) 和help( X ) 可看出 X 對(duì)應(yīng)的對(duì)象里可用的屬性和方法。

X 是 int,那么就是 int 的屬性和方法

X 是 float,那么就是 float 的屬性和方法

等等

dir(int)

['__abs__',

'__add__',

...

'__xor__',

'bit_length',

'conjugate',

...

'real',

'to_bytes']

紅色的是 int 對(duì)象的可用方法,藍(lán)色的是 int 對(duì)象的可用屬性。對(duì)他們你有個(gè)大概印象就可以了,具體怎么用,需要哪些參數(shù) (argument),你還需要查文檔。看個(gè)bit_length的例子

a.bit_length()

11

該函數(shù)是找到一個(gè)整數(shù)的二進(jìn)制表示,再返回其長(zhǎng)度。在本例中 a = 1031, 其二進(jìn)制表示為 ‘10000000111’ ,長(zhǎng)度為 11。

1.2

浮點(diǎn)型

簡(jiǎn)單來(lái)說(shuō),浮點(diǎn)型 (float) 數(shù)就是實(shí)數(shù), 例子如下:

print( 1, type(1) )

print( 1., type(1.) )

1

1.0

加一個(gè)小數(shù)點(diǎn) . 就可以創(chuàng)建 float,不能再簡(jiǎn)單。有時(shí)候我們想保留浮點(diǎn)型的小數(shù)點(diǎn)后 n 位。可以用 decimal 包里的 Decimal 對(duì)象和 getcontext() 方法來(lái)實(shí)現(xiàn)。

import decimal

from decimal import Decimal

Python 里面有很多用途廣泛的包 (package),用什么你就引進(jìn) (import) 什么。包也是對(duì)象,也可以用上面提到的dir(decimal) 來(lái)看其屬性和方法。比如 getcontext() 顯示了 Decimal 對(duì)象的默認(rèn)精度值是 28 位 (prec=28),展示如下:

decimal.getcontext()

Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999,

Emax=999999, capitals=1, clamp=0, flags=[],

traps=[InvalidOperation, DivisionByZero, Overflow])

讓我們看看 1/3 的保留 28 位長(zhǎng)什么樣?

d = Decimal(1) / Decimal(3)

d

Decimal('0.3333333333333333333333333333')

那保留 4 位呢?用 getcontext().prec 來(lái)調(diào)整精度哦。

decimal.getcontext().prec = 4 

e = Decimal(1) / Decimal(3)

e

Decimal('0.3333')

高精度的 float 加上低精度的 float,保持了高精度,沒(méi)毛病。

d + e

Decimal('0.6666333333333333333333333333')

1.3

布爾型

布爾 (boolean) 型變量只能取兩個(gè)值,True 和 False。當(dāng)把布爾變量用在數(shù)字運(yùn)算中,用 1 和 0 代表 True 和 False。

T = True

F = False

print( T + 2 )

print( F - 8 )

3

-8

除了直接給變量賦值 True 和 False,還可以用 bool(X) 來(lái)創(chuàng)建變量,其中 X 可以是

基本類型:整型、浮點(diǎn)型、布爾型

容器類型:字符、元組、列表、字典和集合

基本類型

print( type(0), bool(0), bool(1) )

print( type(10.31), bool(0.00), bool(10.31) )

print( type(True), bool(False), bool(True) )

False True

False True

False True

bool 作用在基本類型變量的總結(jié):X 只要不是整型 0、浮點(diǎn)型 0.0,bool(X) 就是 True,其余就是 False。

容器類型

print( type(''), bool( '' ), bool( 'python' ) )

print( type(()), bool( () ), bool( (10,) ) )

print( type([]), bool( [] ), bool( [1,2] ) )

print( type({}), bool( {} ), bool( {'a':1, 'b':2} ) )

print( type(set()), bool( set() ), bool( {1,2} ) )

False True

False True

False True

False True

False True

bool 作用在容器類型變量的總結(jié):X 只要不是空的變量,bool(X) 就是 True,其余就是 False。

知識(shí)點(diǎn)

確定bool(X) 的值是 True 還是 False,就看 X 是不是空,空的話就是 False,不空的話就是 True。

對(duì)于數(shù)值變量,0, 0.0 都可認(rèn)為是空的。

對(duì)于容器變量,里面沒(méi)元素就是空的。

此外兩個(gè)布爾變量 P 和 Q 的邏輯運(yùn)算的結(jié)果總結(jié)如下表:

2容器數(shù)據(jù)類型

上節(jié)介紹的整型、浮點(diǎn)型和布爾型都可以看成是單獨(dú)數(shù)據(jù),而這些數(shù)據(jù)都可以放在一個(gè)容器里得到一個(gè)「容器類型」的數(shù)據(jù),比如:

字符 (str) 是一容器的字節(jié) char,注意 Python 里面沒(méi)有 char 類型的數(shù)據(jù),可以把單字符的 str 當(dāng)做 char。

元組 (tuple)、列表 (list)、字典 (dict) 和集合 (set) 是一容器的任何類型變量。

2.1

字符

字符用于處理文本 (text) 數(shù)據(jù),用「單引號(hào) ’」和「雙引號(hào) “」來(lái)定義都可以。

創(chuàng)建字符

t1 = 'i love Python!'

print( t1, type(t1) )

t2 = "I love Python!"

print( t2, type(t2) )

i love Python!

I love Python!

字符中常見(jiàn)的內(nèi)置方法 (可以用 dir(str) 來(lái)查) 有

capitalize():大寫句首的字母

split():把句子分成單詞

find(x):找到給定詞 x 在句中的索引,找不到返回 -1

replace(x, y):把句中 x 替代成 y

strip(x):刪除句首或句末含 x 的部分

t1.capitalize()

'I love python!'

t2.split()

['I', 'love', 'Python!']

print( t1.find('love') )

print( t1.find('like') )

2

-1

t2.replace( 'love Python', 'hate R' )

'I hate R!'

print( 'http://www.python.org'.strip('htp:/') )

print( 'http://www.python.org'.strip('.org') )

www.python.org

http://www.python

索引和切片

s = 'Python'

print( s )

print( s[2:4] )

print( s[-5:-2] )

print( s[2] )

print( s[-1] )

Python

th

yth

t

n

知識(shí)點(diǎn)

Python 里面索引有三個(gè)特點(diǎn) (經(jīng)常讓人困惑):

從 0 開(kāi)始 (和 C 一樣),不像 Matlab 從 1 開(kāi)始。

切片通常寫成 start:end 這種形式,包括「start 索引」對(duì)應(yīng)的元素,不包括「end索引」對(duì)應(yīng)的元素。因此 s[2:4] 只獲取字符串第 3 個(gè)到第 4 個(gè)元素。

索引值可正可負(fù),正索引從 0 開(kāi)始,從左往右;負(fù)索引從 -1 開(kāi)始,從右往左。使用負(fù)數(shù)索引時(shí),會(huì)從最后一個(gè)元素開(kāi)始計(jì)數(shù)。最后一個(gè)元素的位置編號(hào)是 -1。

這些特點(diǎn)引起讀者對(duì)切片得到什么樣的元素感到困惑。有個(gè)小竅門可以幫助大家快速鎖定切片的元素,如下圖。

與其把注意力放在元素對(duì)應(yīng)的索引,不如想象將元素分開(kāi)的隔欄,顯然 6 個(gè)元素需要 7 個(gè)隔欄,隔欄索引也是從 0 開(kāi)始,這樣再看到 start:end 就認(rèn)為是隔欄索引,那么獲取的元素就是「隔欄 start」和「隔欄 end」之間包含的元素。如上圖:

string[2:4] 就是「隔欄 2」和「隔欄 4」之間包含的元素,即 th

string[-5:-2] 就是「隔欄 -5」和「隔欄 -2」之間包含的元素,即 yth

正則表達(dá)式

正則表達(dá)式 (regular expression) 主要用于識(shí)別字符串中符合某種模式的部分,什么叫模式呢?用下面一個(gè)具體例子來(lái)講解。

input = """

'06/18/2019 13:00:00', 100, '1st';

'06/18/2019 13:30:00', 110, '2nd';

'06/18/2019 14:00:00', 120, '3rd'

"""

input

"\n'06/18/2019 13:00:00', 100, '1st';

 \n'06/18/2019 13:30:00', 110, '2nd';

 \n'06/18/2019 14:00:00', 120, '3rd'\n"

假如你想把上面字符串中的「時(shí)間」的模式來(lái)抽象的表示出來(lái),對(duì)照著具體表達(dá)式 '06/18/2019 13:00:00' 來(lái)看,我們發(fā)現(xiàn)該字符串有以下規(guī)則:

開(kāi)頭和結(jié)束都有個(gè)單引號(hào) '

里面有多個(gè) 0-9 數(shù)字

里面有多個(gè)正斜線 / 和分號(hào) : 

還有一個(gè)空格

因此我們用下面這樣的模式

pattern = re.compile("'[0-9/:\s]+'")

再看這個(gè)抽象模式表達(dá)式 '[0-9/:\s]+',里面符號(hào)的意思如下:

最外面的兩個(gè)單引號(hào) ' 代表該模式以它們開(kāi)始和結(jié)束

中括號(hào) [] 用來(lái)概括該模式涵蓋的所有類型的字節(jié)

0-9 代表數(shù)字類的字節(jié)

/ 代表正斜線

: 代表分號(hào)

\s 代表空格

[] 外面的加號(hào) + 代表 [] 里面的字節(jié)出現(xiàn)至少 1 次

有了模式 pattern,我們來(lái)看看是否能把字符串中所有符合 pattern 的日期表達(dá)式都找出來(lái)。

pattern.findall(input)

["'06/18/2019 13:00:00'",

 "'06/18/2019 13:30:00'",

 "'06/18/2019 14:00:00'"]

結(jié)果是對(duì)的,之后你想怎么盤它就是你自己的事了,比如把 / 換成 -,比如用 datetime 里面的 striptime() 把日期里年、月、日、小時(shí)、分鐘和秒都獲取出來(lái)。

2.2

元組

創(chuàng)建元組

「元組」定義語(yǔ)法為 

(元素1, 元素2, ..., 元素n)

關(guān)鍵點(diǎn)是「小括號(hào) ()」和「逗號(hào) ,」

小括號(hào)把所有元素綁在一起

逗號(hào)將每個(gè)元素一一分開(kāi)

創(chuàng)建元組的例子如下:

t1 = (1, 10.31, 'python')

t2 = 1, 10.31, 'python'

print( t1, type(t1) )

print( t2, type(t2) )

(1, 10.31, 'python')

(1, 10.31, 'python')

知識(shí)點(diǎn)

創(chuàng)建元組可以用小括號(hào) (),也可以什么都不用,為了可讀性,建議還是用 ()。此外對(duì)于含單個(gè)元素的元組,務(wù)必記住要多加一個(gè)逗號(hào),舉例如下:

print( type( ('OK') ) )  # 沒(méi)有逗號(hào) , 

print( type( ('OK',) ) ) # 有逗號(hào) ,

看看,沒(méi)加逗號(hào)來(lái)創(chuàng)建含單元素的元組,Python 認(rèn)為它是字符。

當(dāng)然也可以創(chuàng)建二維元組:

nested = (1, 10.31, 'python'), ('data', 11)

nested

((1, 10.31, 'python'), ('data', 11))

索引和切片

元組中可以用整數(shù)來(lái)對(duì)它進(jìn)行索引 (indexing) 和切片 (slicing),不嚴(yán)謹(jǐn)?shù)闹v,前者是獲取單個(gè)元素,后者是獲取一組元素。接著上面二維元組的例子,先看看索引的代碼:

nested[0]

print( nested[0][0], nested[0][1], nested[0][2] )

(1, 10.31, 'python')

1 10.31 python

再看看切片的代碼:

nested[0][0:2] 

(1, 10.31)

不可更改

元組有不可更改 (immutable) 的性質(zhì),因此不能直接給元組的元素賦值,例子如下 (注意「元組不支持元素賦值」的報(bào)錯(cuò)提示)。

t = ('OK', [1, 2], True)

t[2] = False

TypeError: 'tuple' object does not support item assignment

但是只要元組中的元素可更改 (mutable),那么我們可以直接更改其元素,注意這跟賦值其元素不同。如下例 t[1] 是列表,其內(nèi)容可以更改,因此用 append 在列表后加一個(gè)值沒(méi)問(wèn)題。

t[1].append(3)

('OK', [1, 2, 3], True)

內(nèi)置方法

元組大小和內(nèi)容都不可更改,因此只有 count 和 index 兩種方法。

t = (1, 10.31, 'python')

print( t.count('python') )

print( t.index(10.31) )

1

1

這兩個(gè)方法返回值都是 1,但意思完全不同

count('python') 是記錄在元組 t 中該元素出現(xiàn)幾次,顯然是 1 次

index(10.31) 是找到該元素在元組 t 的索引,顯然是 1

元組拼接

元組拼接 (concatenate) 有兩種方式,用「加號(hào) +」和「乘號(hào) *」,前者首尾拼接,后者復(fù)制拼接。

(1, 10.31, 'python') + ('data', 11) + ('OK',)

(1, 10.31, 'python') * 2

(1, 10.31, 'python', 'data', 11, 'OK')

(1, 10.31, 'python', 1, 10.31, 'python')

解壓元組

解壓 (unpack) 一維元組 (有幾個(gè)元素左邊括號(hào)定義幾個(gè)變量)

t = (1, 10.31, 'python')

(a, b, c) = t

print( a, b, c )

1 10.31 python

解壓二維元組 (按照元組里的元組結(jié)構(gòu)來(lái)定義變量)

t = (1, 10.31, ('OK','python'))

(a, b, (c,d)) = t

print( a, b, c, d )

1 10.31 OK python

如果你只想要元組其中幾個(gè)元素,用通配符「*」,英文叫 wildcard,在計(jì)算機(jī)語(yǔ)言中代表一個(gè)或多個(gè)元素。下例就是把多個(gè)元素丟給了 rest 變量。

t = 1, 2, 3, 4, 5

a, b, *rest, c = t

print( a, b, c )

print( rest )

1 2 5

[3, 4]

如果你根本不在乎 rest 變量,那么就用通配符「*」加上下劃線「_」,劉例子如下:

a, b, *_ = t

print( a, b )

1 2

優(yōu)點(diǎn)缺點(diǎn)

優(yōu)點(diǎn):占內(nèi)存小,安全,創(chuàng)建遍歷速度比列表快,可一賦多值。

缺點(diǎn):不能添加和更改元素。

等等等,這里有點(diǎn)矛盾,元組的不可更改性即使優(yōu)點(diǎn) (安全) 有時(shí)缺點(diǎn)?確實(shí)是這樣的,安全就沒(méi)那么靈活,靈活就沒(méi)那么安全。看看大佬廖雪峰怎么評(píng)價(jià)「不可更改性」吧

immutable 的好處實(shí)在是太多了:性能優(yōu)化,多線程安全,不需要鎖,不擔(dān)心被惡意修改或者不小心修改。

后面那些安全性的東西我也不大懂,性能優(yōu)化這個(gè)我可以來(lái)測(cè)試一下列表和元組。列表雖然沒(méi)介紹,但是非常簡(jiǎn)單,把元組的「小括號(hào) ()」該成「中括號(hào) []」就是列表了。我們從創(chuàng)建、遍歷和占空間三方面比較。

創(chuàng)建

%timeit [1, 2, 3, 4, 5]

%timeit (1, 2, 3, 4, 5)

62 ns ± 13.2 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

12.9 ns ± 1.94 ns per loop (mean ± std. dev. of 7 runs, 100000000 loops each)

創(chuàng)建速度,元組 (12.9ns) 碾壓列表 (62ns)。

遍歷

lst = [i for i in range(65535)]

tup = tuple(i for i in range(65535))

%timeit for each in lst: pass

%timeit for each in tup: pass

507 μs ± 61.1 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

498 μs ± 18.7 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

遍歷速度兩者相當(dāng),元組 (498 μs) 險(xiǎn)勝列表 (507 μs)。

占空間

from sys import getsizeof

print( getsizeof(lst) )

print( getsizeof(tup) )

578936

524328

列表比元組稍微廢點(diǎn)內(nèi)存空間。

2.3

列表

創(chuàng)建列表

「列表」定義語(yǔ)法為 

[元素1, 元素2, ..., 元素n]

關(guān)鍵點(diǎn)是「中括號(hào) []」和「逗號(hào) ,」

中括號(hào)把所有元素綁在一起

逗號(hào)將每個(gè)元素一一分開(kāi)

創(chuàng)建列表的例子如下:

l = [1, 10.31,'python']

print(l, type(l))

[1, 10.31, 'python']

內(nèi)置方法

不像元組,列表內(nèi)容可更改 (mutable),因此附加 (append, extend)、插入 (insert)、刪除 (remove, pop) 這些操作都可以用在它身上。

附加

l.append([4, 3])

print( l )

l.extend([1.5, 2.0, 'OK'])

print( l )

[1, 10.31, 'python', [4, 3]]

[1, 10.31, 'python', [4, 3], 1.5, 2.0, 'OK']

嚴(yán)格來(lái)說(shuō) append 是追加,把一個(gè)東西整體添加在列表后,而 extend 是擴(kuò)展,把一個(gè)東西里的所有元素添加在列表后。對(duì)著上面結(jié)果感受一下區(qū)別。

插入

l.insert(1, 'abc') # insert object before the index position

l

[1, 'abc', 10.31, 'python', [4, 3], 1.5, 2.0, 'OK']

insert(i, x) 在編號(hào) i 位置前插入 x。對(duì)著上面結(jié)果感受一下。

刪除

l.remove('python') # remove first occurrence of object

l

[1, 'abc', 10.31, [4, 3], 1.5, 2.0, 'OK']

p = l.pop(3) # removes and returns object at index.  Only only pop 1 index position at any time.

print( p )

print( l ) 

[4, 3]

[1, 'abc', 10.31, 1.5, 2.0, 'OK']

remove 和 pop 都可以刪除元素

前者是指定具體要?jiǎng)h除的元素,比如 'python'

后者是指定一個(gè)編號(hào)位置,比如 3,刪除 l[3] 并返回出來(lái)

對(duì)著上面結(jié)果感受一下,具體用哪個(gè)看你需求。

切片索引

索引 (indexing) 和切片 (slicing) 語(yǔ)法在元組那節(jié)都講了,而且怎么判斷切片出來(lái)的元素在字符那節(jié)也講了,規(guī)則如下圖:

對(duì)照上圖看下面兩個(gè)例子 (順著數(shù)和倒著數(shù)編號(hào)):

l = [7, 2, 9, 10, 1, 3, 7, 2, 0, 1]

l[1:5]

[2, 9, 10, 1]

l[-4:]

[7, 2, 0, 1]

列表可更改,因此可以用切片來(lái)賦值。

l[2:4] = [999, 1000]

l

[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1]

切片的通用寫法是

start : stop : step

這三個(gè)在特定情況下都可以省去,我們來(lái)看看四種情況:

情況 1 - start : 

print( l )

print( l[3:] )

print( l[-4:] )

[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1]

[1000, 1, 3, 7, 2, 0, 1]

[7, 2, 0, 1]

以 step 為 1 (默認(rèn)) 從編號(hào) start 往列表尾部切片。

郵箱
huangbenjincv@163.com

安顺市| 平顶山市| 阿城市| 屏东市| 阿克苏市| 武城县| 裕民县| 景东| 磐石市| 泗阳县| 县级市| 揭西县| 兴义市| 绿春县| 西贡区| 邯郸市| 塘沽区| 乾安县| 二连浩特市| 靖边县| 手游| 应用必备| 梁河县| 达孜县| 韶山市| 社旗县| 章丘市| 罗山县| 五台县| 民和| 安塞县| 平武县| 宁远县| 平邑县| 镇江市| 曲沃县| 梅河口市| 手机| 大关县| 汤阴县| 调兵山市|