選自《王的機器》 作者:王圣元 https://mp.weixin.qq.com/s/c5NEDrTMOm8MjuM67HJ1aQ 微信公眾號終于可以插代碼了,Python 可以走一波了。首先我承認不是硬核搞 IT 的,太高級的玩法也玩不來,講講下面基本的還可以,之后帶點機器學(xué)習(xí)、金融工程和量化投資的實例也是可以。 這個系列力求精簡和實用 (可能不會完整,但看完此貼舉一反三也不要完整,追求完整的建議去看書),到了「難點處」我一定會畫圖幫助讀者理解。Python 系列的入門篇的目錄如下,本帖是上篇,只涵蓋前三個節(jié),下篇接著后兩節(jié)。 對于任何一種計算機語言,我覺得最重要的就是「數(shù)據(jù)類型」「條件語句 & 迭代循環(huán)」和「函數(shù)」,這三方面一定要打牢基礎(chǔ)。此外 Python 非常簡潔,一行代碼 (one-liner) 就能做很多事情,很多時候都用了各種「解析式」,比如列表、字典和集合解析式。 在學(xué)習(xí)本貼前感受一下這個問題:如何把以下這個不規(guī)則的列表 a 里的所有元素一個個寫好,專業(yè)術(shù)語叫打平 (flatten)? a = [1, 2, [3, 4], [[5, 6], [7, 8]]] 魔法來了 (這一行代碼有些長,用手機的建議橫屏看)
[1, 2, 3, 4, 5, 6, 7, 8] 這一行代碼,用到了迭代、匿名函數(shù)、遞推函數(shù)、解析式這些技巧。初學(xué)者一看只會說“好酷啊,但看不懂”,看完本帖和下帖后,我保證你會說“我也會這樣用了,真酷!” Python 里面有自己的內(nèi)置數(shù)據(jù)類型 (build-in data type),本節(jié)介紹基本的三種,分別是整型 (int),浮點型 (float),和布爾型 (bool)。 1.1 整型 整數(shù) (integer) 是最簡單的數(shù)據(jù)類型,和下面浮點數(shù)的區(qū)別就是前者小數(shù)點后沒有值,后者小數(shù)點后有值。例子如下:
1031 <class 'int'> 通過 print 的可看出 a 的值,以及類 (class) 是 int。Python 里面萬物皆對象(object),「整數(shù)」也不例外,只要是對象,就有相應(yīng)的屬性 (attributes) 和方法 (methods)。 通過 dir( X ) 和help( X ) 可看出 X 對應(yīng)的對象里可用的屬性和方法。
等等
['__abs__', 紅色的是 int 對象的可用方法,藍色的是 int 對象的可用屬性。對他們你有個大概印象就可以了,具體怎么用,需要哪些參數(shù) (argument),你還需要查文檔??磦€bit_length的例子
11 該函數(shù)是找到一個整數(shù)的二進制表示,再返回其長度。在本例中 a = 1031, 其二進制表示為 ‘10000000111’ ,長度為 11。 1.2 浮點型 簡單來說,浮點型 (float) 數(shù)就是實數(shù), 例子如下:
1 <class 'int'> 加一個小數(shù)點 . 就可以創(chuàng)建 float,不能再簡單。有時候我們想保留浮點型的小數(shù)點后 n 位。可以用 decimal 包里的 Decimal 對象和 getcontext() 方法來實現(xiàn)。
Python 里面有很多用途廣泛的包 (package),用什么你就引進 (import) 什么。包也是對象,也可以用上面提到的dir(decimal) 來看其屬性和方法。比如 getcontext() 顯示了 Decimal 對象的默認精度值是 28 位 (prec=28),展示如下: decimal.getcontext()
讓我們看看 1/3 的保留 28 位長什么樣? d = Decimal(1) / Decimal(3) d
那保留 4 位呢?用 getcontext().prec 來調(diào)整精度哦。 decimal.getcontext().prec = 4 e = Decimal(1) / Decimal(3) e
高精度的 float 加上低精度的 float,保持了高精度,沒毛病。 d + e
1.3 布爾型 布爾 (boolean) 型變量只能取兩個值,True 和 False。當(dāng)把布爾變量用在數(shù)字運算中,用 1 和 0 代表 True 和 False。 T = True F = False print( T + 2 ) print( F - 8 )
除了直接給變量賦值 True 和 False,還可以用 bool(X) 來創(chuàng)建變量,其中 X 可以是
print( type(0), bool(0), bool(1) ) print( type(10.31), bool(0.00), bool(10.31) ) print( type(True), bool(False), bool(True) )
bool 作用在基本類型變量的總結(jié):X 只要不是整型 0、浮點型 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} ) )
bool 作用在容器類型變量的總結(jié):X 只要不是空的變量,bool(X) 就是 True,其余就是 False。 確定bool(X) 的值是 True 還是 False,就看 X 是不是空,空的話就是 False,不空的話就是 True。
此外兩個布爾變量 P 和 Q 的邏輯運算的結(jié)果總結(jié)如下表: 上節(jié)介紹的整型、浮點型和布爾型都可以看成是單獨數(shù)據(jù),而這些數(shù)據(jù)都可以放在一個容器里得到一個「容器類型」的數(shù)據(jù),比如:
2.1 字符 字符用于處理文本 (text) 數(shù)據(jù),用「單引號 ’」和「雙引號 “」來定義都可以。 t1 = 'i love Python!' print( t1, type(t1) ) t2 = 'I love Python!' print( t2, type(t2) )
字符中常見的內(nèi)置方法 (可以用 dir(str) 來查) 有
t1.capitalize()
t2.split()
print( t1.find('love') ) print( t1.find('like') )
t2.replace( 'love Python', 'hate R' )
print( 'http://www.'.strip('htp:/') ) print( 'http://www.'.strip('.org') )
s = 'Python' print( s ) print( s[2:4] ) print( s[-5:-2] ) print( s[2] ) print( s[-1] )
Python 里面索引有三個特點 (經(jīng)常讓人困惑):
這些特點引起讀者對切片得到什么樣的元素感到困惑。有個小竅門可以幫助大家快速鎖定切片的元素,如下圖。 與其把注意力放在元素對應(yīng)的索引,不如想象將元素分開的隔欄,顯然 6 個元素需要 7 個隔欄,隔欄索引也是從 0 開始,這樣再看到 start:end 就認為是隔欄索引,那么獲取的元素就是「隔欄 start」和「隔欄 end」之間包含的元素。如上圖:
正則表達式 (regular expression) 主要用于識別字符串中符合某種模式的部分,什么叫模式呢?用下面一個具體例子來講解。 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
假如你想把上面字符串中的「時間」的模式來抽象的表示出來,對照著具體表達式 '06/18/2019 13:00:00' 來看,我們發(fā)現(xiàn)該字符串有以下規(guī)則:
因此我們用下面這樣的模式 pattern = re.compile(''[0-9/:\s]+'') 再看這個抽象模式表達式 '[0-9/:\s]+',里面符號的意思如下:
有了模式 pattern,我們來看看是否能把字符串中所有符合 pattern 的日期表達式都找出來。
[''06/18/2019 13:00:00'', 結(jié)果是對的,之后你想怎么盤它就是你自己的事了,比如把 / 換成 -,比如用 datetime 里面的 striptime() 把日期里年、月、日、小時、分鐘和秒都獲取出來。 2.2 元組 「元組」定義語法為 (元素1, 元素2, ..., 元素n) 關(guān)鍵點是「小括號 ()」和「逗號 ,」
創(chuàng)建元組的例子如下:
(1, 10.31, 'python') <class 'tuple'> 創(chuàng)建元組可以用小括號 (),也可以什么都不用,為了可讀性,建議還是用 ()。此外對于含單個元素的元組,務(wù)必記住要多加一個逗號,舉例如下:
<class 'str'> 看看,沒加逗號來創(chuàng)建含單元素的元組,Python 認為它是字符。 當(dāng)然也可以創(chuàng)建二維元組:
((1, 10.31, 'python'), ('data', 11)) 元組中可以用整數(shù)來對它進行索引 (indexing) 和切片 (slicing),不嚴謹?shù)闹v,前者是獲取單個元素,后者是獲取一組元素。接著上面二維元組的例子,先看看索引的代碼:
(1, 10.31, 'python') 再看看切片的代碼:
(1, 10.31) 元組有不可更改 (immutable) 的性質(zhì),因此不能直接給元組的元素賦值,例子如下 (注意「元組不支持元素賦值」的報錯提示)。
TypeError: 'tuple' object does not support item assignment 但是只要元組中的元素可更改 (mutable),那么我們可以直接更改其元素,注意這跟賦值其元素不同。如下例 t[1] 是列表,其內(nèi)容可以更改,因此用 append 在列表后加一個值沒問題。
('OK', [1, 2, 3], True) 元組大小和內(nèi)容都不可更改,因此只有 count 和 index 兩種方法。
1 這兩個方法返回值都是 1,但意思完全不同
元組拼接 (concatenate) 有兩種方式,用「加號 +」和「乘號 *」,前者首尾拼接,后者復(fù)制拼接。
(1, 10.31, 'python', 'data', 11, 'OK') 解壓 (unpack) 一維元組 (有幾個元素左邊括號定義幾個變量)
1 10.31 python 解壓二維元組 (按照元組里的元組結(jié)構(gòu)來定義變量)
1 10.31 OK python 如果你只想要元組其中幾個元素,用通配符「*」,英文叫 wildcard,在計算機語言中代表一個或多個元素。下例就是把多個元素丟給了 rest 變量。
1 2 5 如果你根本不在乎 rest 變量,那么就用通配符「*」加上下劃線「_」,劉例子如下:
1 2 優(yōu)點:占內(nèi)存小,安全,創(chuàng)建遍歷速度比列表快,可一賦多值。 缺點:不能添加和更改元素。 等等等,這里有點矛盾,元組的不可更改性即使優(yōu)點 (安全) 有時缺點?確實是這樣的,安全就沒那么靈活,靈活就沒那么安全??纯创罄辛窝┓逶趺丛u價「不可更改性」吧 immutable 的好處實在是太多了:性能優(yōu)化,多線程安全,不需要鎖,不擔(dān)心被惡意修改或者不小心修改。 后面那些安全性的東西我也不大懂,性能優(yōu)化這個我可以來測試一下列表和元組。列表雖然沒介紹,但是非常簡單,把元組的「小括號 ()」該成「中括號 []」就是列表了。我們從創(chuàng)建、遍歷和占空間三方面比較。
62 ns ± 13.2 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 創(chuàng)建速度,元組 (12.9ns) 碾壓列表 (62ns)。
507 μs ± 61.1 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 遍歷速度兩者相當(dāng),元組 (498 μs) 險勝列表 (507 μs)。
578936 列表比元組稍微廢點內(nèi)存空間。 2.3 列表 「列表」定義語法為 [元素1, 元素2, ..., 元素n] 關(guān)鍵點是「中括號 []」和「逗號 ,」
創(chuàng)建列表的例子如下:
[1, 10.31, 'python'] <class 'list'> 不像元組,列表內(nèi)容可更改 (mutable),因此附加 (append, extend)、插入 (insert)、刪除 (remove, pop) 這些操作都可以用在它身上。
[1, 10.31, 'python', [4, 3]] 嚴格來說 append 是追加,把一個東西整體添加在列表后,而 extend 是擴展,把一個東西里的所有元素添加在列表后。對著上面結(jié)果感受一下區(qū)別。
[1, 'abc', 10.31, 'python', [4, 3], 1.5, 2.0, 'OK'] insert(i, x) 在編號 i 位置前插入 x。對著上面結(jié)果感受一下。
[1, 'abc', 10.31, [4, 3], 1.5, 2.0, 'OK']
[4, 3] remove 和 pop 都可以刪除元素
對著上面結(jié)果感受一下,具體用哪個看你需求。 索引 (indexing) 和切片 (slicing) 語法在元組那節(jié)都講了,而且怎么判斷切片出來的元素在字符那節(jié)也講了,規(guī)則如下圖: 對照上圖看下面兩個例子 (順著數(shù)和倒著數(shù)編號):
[2, 9, 10, 1]
[7, 2, 0, 1] 列表可更改,因此可以用切片來賦值。
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] 切片的通用寫法是 start : stop : step 這三個在特定情況下都可以省去,我們來看看四種情況:
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] 以 step 為 1 (默認) 從編號 start 往列表尾部切片。
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] 以 step 為 1 (默認) 從列表頭部往編號 stop 切片。
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] 以 step 為 1 (默認) 從編號 start 往編號 stop 切片。
[7, 2, 999, 1000, 1, 3, 7, 2, 0, 1] 以具體的 step 從編號 start 往編號 stop 切片。注意最后把 step 設(shè)為 -1,相當(dāng)于將列表反向排列。 和元組拼接一樣, 列表拼接也有兩種方式,用「加號 +」和「乘號 *」,前者首尾拼接,后者復(fù)制拼接。
[1, 10.31, 'python', 'data', 11, 'OK'] 優(yōu)點:靈活好用,可索引、可切片、可更改、可附加、可插入、可刪除。 缺點:相比 tuple 創(chuàng)建和遍歷速度慢,占內(nèi)存。此外查找和插入時間較慢。 2.4 字典 「字典」定義語法為 {元素1, 元素2, ..., 元素n} 其中每一個元素是一個「鍵值對」- 鍵:值 (key:value) 關(guān)鍵點是「大括號 {}」,「逗號 ,」和「分號 :」
創(chuàng)建字典的例子如下:
{'Name': 'Tencent', 'Country': 'China', 字典里最常用的三個內(nèi)置方法就是 keys(), values() 和 items(),分別是獲取字典的鍵、值、對。
['Name', 'Country', 'Industry', 'Code', 'Price', 'Headquarter'] 此外在字典上也有添加、獲取、更新、刪除等操作。 比如加一個「總部:深圳」
{'Name': 'Tencent', 比如想看看騰訊的股價是多少 (兩種方法都可以)
359 HKD 比如更新騰訊的股價到 359 港幣
{'Name': 'Tencent', 比如去掉股票代碼 (code)
{'Name': 'Tencent', 或像列表里的 pop() 函數(shù),刪除行業(yè) (industry) 并返回出來。
Technology 字典里的鍵是不可更改的,因此只有那些不可更改的數(shù)據(jù)類型才能當(dāng)鍵,比如整數(shù) (雖然怪怪的)、浮點數(shù) (雖然怪怪的)、布爾 (雖然怪怪的)、字符、元組 (雖然怪怪的),而列表卻不行,因為它可更改??磦€例子
{2: 'integer key', 雖然怪怪的,但這些 2, 10.31, True, ('OK', 3) 確實能當(dāng)鍵。有個地方要注意下,True 其實和整數(shù) 1 是一樣的,由于鍵不能重復(fù),當(dāng)你把 2 該成 1時,你會發(fā)現(xiàn)字典只會取其中一個鍵,示例如下:
{1: 'boolean key', 那么如何快速判斷一個數(shù)據(jù)類型 X 是不是可更改的呢?兩種方法:
先看用 id() 函數(shù)的在整數(shù) i 和列表 l 上的運行結(jié)果:
1607630928
2022027856840
先看用 hash() 函數(shù)的在字符 s,元組 t 和列表 l 上的運行結(jié)果:
7230166658767143139
3642952815031607597
TypeError: unhashable type: 'list' 字符 s 和元組 t 都能被哈希,因此它們是不可更改的。列表 l 不能被哈希,因此它是可更改的。 優(yōu)點:查找和插入速度快 缺點:占內(nèi)存大 2.5 集合 「集合」有兩種定義語法,第一種是 {元素1, 元素2, ..., 元素n} 關(guān)鍵點是「大括號 {}」和「逗號 ,」
第二種是用 set() 函數(shù),把列表或元組轉(zhuǎn)換成集合。 set( 列表 或 元組 ) 創(chuàng)建集合的例子如下 (用兩者方法創(chuàng)建 A 和 B):
{'d', 'du', 'u', 'ud'} 從 A 的結(jié)果發(fā)現(xiàn)集合的兩個特點:無序 (unordered) 和唯一 (unique)。由于 set 存儲的是無序集合,所以我們沒法通過索引來訪問,但是可以判斷一個元素是否在集合中。
TypeError: 'set' object does not support indexing
True 用 set 的內(nèi)置方法就把它當(dāng)成是數(shù)學(xué)上的集,那么并集、交集、差集都可以玩通了。
{'uu', 'dd', 'd', 'u', 'du', 'ud'}
{'d', 'u'}
{'ud', 'du'}
{'uu', 'dd'}
{'ud', 'du', 'dd', 'uu'} 優(yōu)點:不用判斷重復(fù)的元素 缺點:不能存儲可變對象 你看集合的「唯一」特性還不是雙刃劍,既可以是優(yōu)點,又可以是缺點,所有東西都有 trade-off 的,要不然它就沒有存在的必要了。 在編寫程序時,我們要
3.1 條件語句 條件語句太簡單了,大體有四種格式
看了下面四幅圖 (包含代碼) 應(yīng)該秒懂條件語句,其實任何會說話的人都應(yīng)該懂它。
給定二元條件,滿足做事,不滿足不做事。 if x % 2 == 0: print( 'x is even' ) else : print( 'x is odd' ) 給定二元條件,滿足做事 A,不滿足做事 B。
給定多元條件,滿足條件 1 做事 A1,滿足條件 2 做事 A2,..., 滿足條件 n 做事 An。直到把所有條件遍歷完。 if x == y: print( 'x and y are equal' ) else: if x < y: print( 'x is less than y' ) else: print( 'x is greater than y' ) 給定多元條件,滿足條件 1 做事 A1,不滿足就 給定多元條件,滿足條件 2 做事 A2,不滿足就 ... 直到把所有條件遍歷完。 3.2 迭代循環(huán) 對于迭代循環(huán),Python 里面有「while 循環(huán)」和「for 循環(huán)」,沒有「do-while 循環(huán)」。
5 While 循環(huán)非常簡單,做事直到 while 后面的語句為 False。上例就是打印從 n (初始值為 5) 一直到 1,循環(huán)執(zhí)行了 5 次。 一般來說,在 「while 循環(huán)」中,迭代的次數(shù)事先是不知道的,因為通常你不知道 while 后面的語句從 True 變成 False了。 更多時候我們希望事先直到循環(huán)的次數(shù),比如在列表、元組、字典等容器類數(shù)據(jù)上遍歷一遍,在每個元素層面上做點事情。這時候就需要「for 循環(huán)」了。
I love Python 讀讀 Python 里面的「for 循環(huán)」是不是很像讀英文。通用形式的 for loop 如下 for a in A do something with a 其中 for 和 in 是關(guān)鍵詞,A 是個可迭代數(shù)據(jù) (list, tuple, dic, set),a 是 A 里面的每個元素,上句翻譯成中文是 對于 A 里面的每個 a 對 a 搞點事 回到具體例子,for loop 里面的 language 變量在每次循環(huán)中分別取值 Python, R, Matlab 和 C++,然后被打印。 最后介紹一個稍微有點特殊的函數(shù) enumerate(),和 for loop 一起用的語法如下 for i, a in enumerate(A) do something with a 發(fā)現(xiàn)區(qū)別了沒?用 enumerate(A) 不僅返回了 A 中的元素,還順便給該元素一個索引值 (默認從 0 開始)。此外,用 enumerate(A, j) 還可以確定索引起始值為 j??聪旅胬?。
1 I love Python 學(xué)習(xí)任何一種都要從最基本開始,基本的東西無外乎數(shù)據(jù)類型、條件語句和遞推循環(huán)。 數(shù)據(jù)類型分兩種:
按照 Python 里「萬物皆對象」的思路,學(xué)習(xí)每一個對象里面的屬性 (attributes) 和方法 (methods),你不需要記住所有,有個大概印象有哪些,通過 dir() 來鎖定具體表達式,再去官網(wǎng)上查詢所有細節(jié)。這么學(xué)真的很系統(tǒng)而簡單。此外學(xué)的時候一定要帶著“它的優(yōu)缺點是什么”這樣的問題,所有東西都有 trade-off,一個滿身都是缺點的東西就沒有存在的必要,既然存在肯定有可取之處。 條件語句 (if, if-else, if-elif-else, nested if) 是為了在不同條件下執(zhí)行不同操作,而迭代循環(huán) (while, for) 是重復(fù)的完成相同操作。 抓住上面大框架,最好還要以目標(biāo)導(dǎo)向 (我學(xué) python 就是為了搞量化交易希望能躺著賺錢),別管這目標(biāo)難度如何,起碼可以保證我累得時候還雞血滿滿不會輕言放棄。這樣我就足夠主動的去學(xué)一樣?xùn)|西并學(xué)精學(xué)深,目標(biāo)越難完成,我主動性就越強。 之后所有的細節(jié)都可以慢慢來,雖然我覺得本篇已經(jīng)挖了不少細節(jié)了,像 hashability,但肯定還有更多等著去挖,半篇帖子就想覆蓋 Python 所有內(nèi)容不是開玩笑嗎?但
下篇接著函數(shù) (function) 和解析式 (comprehension)。Stay Tuned! |
|