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




