這是一篇關(guān)于Python量化的筆記~坑的時候有一種很奇怪的感覺,我對python似曾相識,又覺得陌生。打游戲發(fā)現(xiàn)新的隱藏關(guān)卡帶來的是喜悅,而python的坑帶來的是自我懷疑,甚至可能是實實在在的經(jīng)濟(jì)損失,所以,坑,我們踩過的,一定帶你繞過去。
下面讓我們一起看看python隱藏的坑,排名不分先后。 >>>點擊咨詢量化投資行業(yè)前景
1. Python量化之基礎(chǔ)篇
基礎(chǔ)的坑,最重要的是舉一反三,最怕的是在黑板上老師寫了‘一’,回家在筆記本上寫一個橫線就不認(rèn)識這種事情。
忘記寫冒號。在def,if,while,for等語句第一行某位輸入”:”。
縮進(jìn)要一致。避免在縮進(jìn)時混合使用制表符和空格。
不要認(rèn)為在原處修改對象的函數(shù)會返回結(jié)果。例如a = list.append(),a是不會被賦值的。
不要在變量賦值前就使用變量。例如a = b寫這行代碼時,b在此之前一定要被賦值過的。
函數(shù)調(diào)用一定要加括號。例如dataframe.dropna()。
不要寫死循環(huán)。例如while True: 之后的代碼塊中沒有跳出循環(huán)的代碼。
判斷兩個變量是否相等用的是‘==’號。大多數(shù)初學(xué)者,覺得‘=’號就可以了。
寫字符串,寫各種樣式的括號時注意他們是成對出現(xiàn)的。常見['a', b']這樣的錯誤,然后覺得自己代碼沒有問題的初學(xué)者。
>>>點擊咨詢?nèi)绾蜗到y(tǒng)學(xué)習(xí)Python量化投資
2. Python量化之進(jìn)階篇
有一句歌詞唱的挺好的,“跨過這道山,越過那道嶺,眼前又是一座峰”?;A(chǔ)的坑排完了,還會有更多,更隱藏的坑等著你去踩。(比較熟悉這個歌的同學(xué),我覺得你大概率上是北方人,更具體點的話,是東北人)
2.1 賦值僅僅生成引用
a = [1, 2, 3]
b = a
這里你認(rèn)為自己構(gòu)建了兩個相同的列表,準(zhǔn)備用在不同的用途。然而當(dāng)開開心心的進(jìn)行接下來的其他操作時,比如,對a進(jìn)行修改,就發(fā)生了其他的事情。
a[1] = 100000a
out: [1, 100000, 3]
b
out: [1, 100000, 3]
結(jié)果發(fā)現(xiàn),本來希望只對a進(jìn)行修改,結(jié)果發(fā)現(xiàn)b也受到了影響,其原因在于通過b = a的方式進(jìn)行對b賦值,其實a、b指向同一個列表對象。我們可以通過查看a、b的內(nèi)存地址或用is進(jìn)行驗證
print(id(a), id(b))
out: 124006856 124006856
可以看到a、b指向的其實是同一塊內(nèi)存
a is b
out: True
用is也檢測出a和b完全就是一個東西的不同名字而已。
上面的賦值方法還是比較容易看出,因為有等號,那么下面的賦值方法可能就稍微難一點看出來了。
c = [1, a, 3]
c
out: [1, [1, 100000, 3], 3]
當(dāng)對a修改時,c同樣也會受到影響
a.append(100000)
a
out: [1, 100000, 3, 100000]
c
out: [1, [1, 100000, 3, 100000], 3]
所以,不要覺得寫完了,print出來的東西看著和自己想的一樣就萬事大吉,不實際踩一下肯定不知道接下來有坑。
那么,如何解決呢?用 .copy(),這樣就會產(chǎn)生兩個不相干的對象,當(dāng)然如果不嫌麻煩的話,可以把相同的東西再打一遍,然后,你有沒有看到同行鄙視的眼神?
我們看一下效果。
a = [1, 2, 3]
b = a.copy()
a is b
out: False
print(id(a), id(b))
out:125323528 87389000
可以看到a,b指向了不同的內(nèi)存地址,并且用is檢測顯示是不同對象。
接下來修改a。
a[1] = 100000a
out: [1, 100000, 3]
b
out: [1, 2, 3]
可以看到修改a已經(jīng)不會對b產(chǎn)生影響了,此坑已填。
2.2 乘法與嵌套列表
編程的某個時候,你希望生成這樣一個嵌套列表[[],[],[],..., [],[]],里面的列表為空或者默認(rèn)值,那么第一選擇肯定是利用列表乘法一次性生成多個列表,像這樣
a = [[]] * 5a
out: [[], [], [], [], []]
確實滿足了需求,然后當(dāng)你開開心心的使用時,發(fā)覺事情有點不太對。比如對a列表中作為元素的某一個列表進(jìn)行修改
a[0].append(1000)
a[0]
out: [1000]
a
out: [[1000], [1000], [1000], [1000], [1000]]
然后發(fā)現(xiàn),怎么所有作為元素的列表全都發(fā)生了變化。這次同樣可以用id或這is進(jìn)行檢測。
print(id(a[0]), id(a[1]))
out: 125325256 125325256
a[0] is a[1]
out: True
可以看出,原來a列表中作為元素的每一個列表,其實都是同一個東西,這也就解釋了為什么對其中一個作為元素的列表進(jìn)行原地修改時,其他所有作為元素的列表也發(fā)生了變化。
那么,解決方案如下
a = [[] for i in range(5)]
a
out: [[], [], [], [], []]
a[0] is a[1]
out: False
print(id(a[0]), id(a[1]))
out: 125323144 125321544
可以看到,a中作為元素的列表已經(jīng)不是同一個了,這樣對其中的列表進(jìn)行修改時候就不會影響其他列表。
a[0].append(1000)
a
out: [[1000], [], [], [], []]
此坑已填
2.3 本地變量靜態(tài)檢測
剛剛了解作用域的同學(xué)應(yīng)該對LEGB原則有一定了解,然后實踐中可能大膽的寫出了這樣的函數(shù)。
a = 1def print_a():
print(a)
a = 2
這個函數(shù)的目的也比較容易理解,打印a的值之后將a的值修改為2,但是,實際運行時發(fā)生了這樣的事情。
print_a()
---------------------------------------------------------------------------
out: UnboundLocalError Traceback (most recent call last)
----> 1 print_a()
1 a = 1
2 def print_a():
----> 3 print(a)
4 a = 2
UnboundLocalError: local variable 'a' referenced before assignment
發(fā)生這樣問題的原因是在python讀入并編譯這段代碼時,發(fā)現(xiàn)def里面有一個對a賦值的語句,然后決定a在函數(shù)中屬于本地變量名。那么當(dāng)print_a執(zhí)行時,就會對print(a)按照LEGB原則執(zhí)行搜索,發(fā)現(xiàn)a屬于本地變量名但還未賦值。也就是我們在前面基礎(chǔ)坑里面提到的在變量未賦值前進(jìn)行使用。
解決方案需要使用global聲明a是一個全局變量。
a = 1def print_a():
global a
print(a)
a = 2
print_a()
out: 1
a
out: 2
可以看到,函數(shù)已經(jīng)可以正常使用,并且全局變量a按照預(yù)期進(jìn)行了修改。此坑已填。
2.4 可變對象作函數(shù)默認(rèn)參數(shù)
默認(rèn)參數(shù)在def語句運行時完成了保存,對于可變對象而言,當(dāng)函數(shù)被調(diào)用并對該參數(shù)進(jìn)行原地修改,默認(rèn)參數(shù)將發(fā)生變化并進(jìn)而影響接下來的函數(shù)調(diào)用。
先用可變對象作為默認(rèn)參數(shù)編寫一個函數(shù)。
def test(a=[]):
a.append(1)
print(a)
接下來多次調(diào)用test函數(shù),你一定以為每次打出來的都是一個包含1的列表。但是,事實并不是這樣。
test()
out: [1]
test()
out: [1, 1]
test()
out: [1, 1, 1]
是不是感覺三觀盡毀啊。
當(dāng)然,有坑就會有填坑的方案。官方給出的標(biāo)準(zhǔn)解決方案是這樣的。
def test(a=None):
if a is None:
a = []
a.append(1)
print(a)
test()
out: [1]
test()
out: [1]
可以看到,test多次被調(diào)用已經(jīng)不會出現(xiàn)之前的情況了。此坑已填。
2.5 嵌套在循環(huán)中的lambda
在知道python中一切皆對象之后,也許你就會嘗試把一些特別的東西放進(jìn)一個list,雖然不清楚未來實際會不會用到這樣的寫法,反正先試試。然后你就想將一套簡單的函數(shù)放進(jìn)一個列表,然后用lambda編寫。
func_list = []for i in range(5):
func_list.append(lambda x: x + i)
這樣生成了一個元素為函數(shù)的列表,其中每個函數(shù)都可以傳入一個參數(shù),然后返回的是傳入值和0,1,2,3,4的和。當(dāng)然理想情況下是這樣,現(xiàn)實并不會如此。
func_list[0](1)
out: 5
func_list[1](1)
out: 5
func_list[2](1)
out: 5
可以發(fā)現(xiàn),所有結(jié)果都是4+1的和。發(fā)生這種現(xiàn)象的原因在于變量i在函數(shù)調(diào)用時才進(jìn)行查找,而此時循環(huán)已經(jīng)結(jié)束,i=4,所以所有函數(shù)對象在被調(diào)用時參數(shù)i全部都為5.
那么解決方案就是按照本文2.4中寫的,默認(rèn)參數(shù)在def語句運行時完成了保存,也就是使用默認(rèn)參數(shù)的方式解決該問題。
func_list = []for i in range(5):
func_list.append(lambda x, i=i: x + i)
func_list[0](1)
out: 1
func_list[1](1)
out: 2
func_list[2](1)
out: 3
可以看到,問題已經(jīng)得到解決。此坑已填。
2.6 列表和enumerate
熟悉列表,學(xué)習(xí)python算是入門;熟悉enumerate,學(xué)習(xí)python可以說不是純小白了。enumerate返回下標(biāo)和元素的方式還是為python使用者提供了不大不小的便利,不過同時也挖下了一個坑。
比如你寫了一個循環(huán),想從列表中刪除偶數(shù)。
a = [1,2,3,4,5,6]for i, item in enumerate(a): if a[i] % 2==0: del a[i]
a
out: [1, 3, 5]
是不是想說沒什么錯誤啊,然后在給你看看這個。
a = [1,2,6,3,4,4]for i, item in enumerate(a): if a[i] % 2==0: del a[i]
a
out: [1, 6, 3, 4]
enumerate(a)
out:
產(chǎn)生這個問題的原因是在當(dāng)i=1時,a[1]的值為2,符合條件,那么刪除2這個值之后,a整個列表發(fā)生了變化,6這個數(shù)值前進(jìn)一個位置到了a[1]的位置,然后i在執(zhí)行下一次循環(huán)時候值取了2,我們以為應(yīng)該對應(yīng)著檢驗6,其實6被前移一個位置,也就被遺漏了。
下面我們將for 循環(huán)的部分手動執(zhí)行,看現(xiàn)實是不是和我們理解的一樣。
a = [1,2,6,3,4,4]
e = enumerate(a)
e
out:
i, item = e.__next__()
(i, item)
out: (0, 1)
if a[i] % 2==0: del a[i]
a
out: [1, 2, 6, 3, 4, 4]
此時我們可以看到,執(zhí)行完循環(huán)的第一步,a并沒有發(fā)生變化。接下來我們繼續(xù)第二步。
i, item = e.__next__()
(i, item)
out: (1, 2)
if a[i] % 2==0: del a[i]
a
out: [1, 6, 3, 4, 4]
可以發(fā)現(xiàn),a已經(jīng)發(fā)生了變化,那么我們接下來只要看在enumerate提供新的下標(biāo)好元素是不是還按照未調(diào)整的a進(jìn)行提供的。當(dāng)然,可以告訴你們答案,肯定不是了。
i, item = e.__next__()
(i, item)
out: (2, 3)
可以看到6已經(jīng)不是第三個元素, 它通過向前移動一個位置的方式逃過了檢查。這個坑真的是非常難發(fā)現(xiàn),因為有時候碰巧你的運算方式和提供的列表剛剛好結(jié)果是你想要的,然后讓你覺得這樣用就是正確的。這種時候就非??膳铝恕?/p>
這個坑的解決方案可以使用列表解析式添加篩選條件即可。
a = [1, 2, 6, 3, 4, 4]
a = [x for x in a if x%2 != 0]
a
[1, 3]
此坑已填。
是不是覺得覺得一不留神就掉進(jìn)Python的坑里了?哈哈哈,寫到這里,python編程一些比較基礎(chǔ)的坑也已經(jīng)描述的比較詳細(xì)了。我從來不產(chǎn)生失落感,我只是信心的粉碎機(jī)。當(dāng)你覺得自己python已經(jīng)學(xué)的差不多的時候,總會有那么一個人、或者一篇文章告訴你,你懂得還不夠多。如果你想學(xué)習(xí)Python量化培訓(xùn)相關(guān)的內(nèi)容的話,歡迎閱讀這篇文章:求推薦!上海Python量化培訓(xùn)班哪家比較好?
不過,坑總是有的,學(xué)習(xí)的時候你可以抱著填坑的心態(tài),也可以懷著獲取的目的,兩者都取決于你的選擇。而且,個人覺的,懷著獲取的態(tài)度,容易滿足,不斷追求,也就一直快樂。
最后,還是用毒翼神龍的一段話結(jié)個尾:“幾盤卡帶就可以陪我們走過整個童年的那個時代恐怕也一去不復(fù)返了吧。每天的生活被工作、學(xué)習(xí)、應(yīng)酬充斥著,也讓我們漸漸忘記了那個傳說~雖然它是一段美麗的謠言,但想起那個信以為真的年紀(jì),仍然有許多美好的回憶。我懷念那時單純的快樂,懷念那個很容易就能滿足的童心”
.jpg)
金程推薦: AQF就業(yè)前景 AQF年薪 量化金融分析師年薪
AQF考友群:760229148
金融寬客交流群:801860357
微信公眾號:量化金融分析師




