python 获取第一个key_Python中常见的9大坑,看看你有没有遇到
? Python作為一門(mén)簡(jiǎn)潔且容易上手的語(yǔ)言,正在受到越來(lái)越多人的喜愛(ài)。但如果你對(duì)其中的一些細(xì)節(jié)不甚了解,就有可能掉入它的“坑”里。本文將介紹學(xué)習(xí)Python過(guò)程中遇到的一些問(wèn)題,接下來(lái)看看Python里一些常見(jiàn)的坑。
01
三元表達(dá)式
乍一看,按照根深蒂固的四則運(yùn)算的思維,加號(hào)之前是一部分,加號(hào)之后為另一部分,結(jié)果貌似等于10。為什么打印出來(lái)的結(jié)果跟我們預(yù)想的大相徑庭呢?很顯然,Python解釋器在遇到三元表達(dá)式時(shí),默認(rèn)把if之前的(10+4)作為三元表達(dá)式的前面部分了。
02
嵌套列表的創(chuàng)建
要?jiǎng)?chuàng)建一個(gè)嵌套的列表,我們可能會(huì)選擇這樣的方式:
目前看起來(lái)一切正常,我們得到了一個(gè)包含3個(gè)list的嵌套list。接下來(lái)往第一個(gè)list中添加一個(gè)元素:
奇怪的事情發(fā)生了,我們只想給第一元素增加元素,結(jié)果三個(gè)list都增加了一個(gè)元素。這是因?yàn)閇[]]*3并不是創(chuàng)建了三個(gè)不同list,而是創(chuàng)建了三個(gè)指向同一個(gè)list的對(duì)象,所以,當(dāng)我們操作第一個(gè)元素時(shí),其他兩個(gè)元素內(nèi)容也會(huì)發(fā)生變化。下面的代碼可以證實(shí)這一點(diǎn):
正確的做法如下:
03
try...finally + return
看下面這段代碼,可以試想一下print語(yǔ)句打印的順序:
是不是很多小伙伴看到return就對(duì)print的順序感到不知所措了,下圖是最終的結(jié)果:
我們先來(lái)看一段Python官網(wǎng)對(duì)于finally的解釋:
翻譯成中文就是try塊中包含break、continue或者return語(yǔ)句的,在離開(kāi)try塊之前,finally中的語(yǔ)句也會(huì)被執(zhí)行。所以在上面的例子中,try塊return之前,會(huì)執(zhí)行finally中的語(yǔ)句,最后再執(zhí)行try中的return語(yǔ)句返回結(jié)果??吹竭@里小伙伴們都豁然開(kāi)朗了吧。
04
參數(shù)默認(rèn)值
當(dāng)我們把函數(shù)的參數(shù)默認(rèn)值設(shè)置為列表時(shí),會(huì)發(fā)生什么?
出現(xiàn)以上情況的原因是:默認(rèn)值在定義函數(shù)時(shí)計(jì)算(通常在加載模塊時(shí)),因此默認(rèn)值變成了函數(shù)對(duì)象的屬性。具體來(lái)說(shuō),函數(shù)的參數(shù)默認(rèn)值保存在函數(shù)的__defaults__屬性中,指向了同一個(gè)列表。因此,如果默認(rèn)值是可變對(duì)象,而且修改了它的值,那么后續(xù)的函數(shù)調(diào)用都會(huì)受到影響。正確的做法是設(shè)置該參數(shù)默認(rèn)為None。
05
lambda自由參數(shù)之坑
先來(lái)看這樣一段代碼:
結(jié)果并不是0,2,4,6,8,而是8,8,8,8,8。不少人可能會(huì)覺(jué)得匪夷所思,不著急,先試著用dis庫(kù)分析字節(jié)碼。
沒(méi)有得到想要的結(jié)果,只能看到參數(shù)i和x,參數(shù)i的具體值無(wú)法獲取。這也就是說(shuō)lambda函數(shù)在定義的時(shí)候只知道有一個(gè)i,而他的值并不明確,之后通過(guò)計(jì)算獲取i的值。到這里很容易聯(lián)想到閉包,因?yàn)閕引用了“for i in range(5)”這個(gè)表達(dá)式中的值。先復(fù)習(xí)一下“閉包”的定義:閉包是一種函數(shù),它會(huì)保留定義函數(shù)時(shí)存在的自由變量的綁定,這樣調(diào)用函數(shù)時(shí),雖然定義作用域不可用了,但是仍能使用那些綁定。接下來(lái)驗(yàn)證一下,我們通過(guò)f.__code__.co_freevars來(lái)獲取自由變量的名稱(chēng),通過(guò)f.__closure__[0].cell_contents得到自由變量的值:
果不其然,自由變量i最終的值都是4,這也就解釋了最開(kāi)始的結(jié)果。如果還不明白可以看下面這段代碼。
Python程序從上到下執(zhí)行,同時(shí)它也是一門(mén)動(dòng)態(tài)型的語(yǔ)言,舉個(gè)例子,定義一個(gè)類(lèi)之后,你可以動(dòng)態(tài)的給它增加方法。同樣,上面這個(gè)例子中,程序執(zhí)行到最后i的值為5,所以lambda表達(dá)式中i為5,最終的結(jié)果為:[10, 10,10, 10, 10, 10]。
要解決上述出現(xiàn)的問(wèn)題,就要把閉包作用域變?yōu)榫植孔饔糜?#xff1a;
a = [lambda x, i=i: i*x for i in range(5)]。這行代碼等效于下面這種寫(xiě)法:
“紙上得來(lái)終覺(jué)淺,絕知此事要躬行”。
06
含單個(gè)元素的元組
上圖有兩行新建元組的代碼,但只有第二種寫(xiě)法是正確的。因?yàn)樵谖ㄒ坏脑刂蟛患佣禾?hào),小括號(hào)對(duì)于Python解釋器是無(wú)效的。
07
對(duì)象銷(xiāo)毀順序
創(chuàng)建一個(gè)類(lèi)OBJ:
創(chuàng)建兩個(gè)OBJ示例,使用is判斷是否為同一對(duì)象:
接下來(lái)同樣創(chuàng)建兩個(gè)對(duì)象,使用id來(lái)判斷。
調(diào)用id函數(shù), Python 創(chuàng)建一個(gè)OBJ類(lèi)的實(shí)例,并使用id函數(shù)獲得內(nèi)存地址后,銷(xiāo)毀內(nèi)存丟棄這個(gè)對(duì)象。當(dāng)連續(xù)兩次進(jìn)行此操作, Python會(huì)將相同的內(nèi)存地址分配給第二個(gè)對(duì)象,所以?xún)蓚€(gè)對(duì)象的id值是相同的。但是is行為卻與之不同,通過(guò)打印順序就可以看到。
08
了解執(zhí)行時(shí)機(jī)
請(qǐng)看下面這個(gè)例子:
結(jié)果并不是[1, 3, 5]而是[5],這有些不可思議。原因在于,in子句在聲明時(shí)執(zhí)行, 而條件子句則是在運(yùn)行時(shí)執(zhí)行。所以上圖中的生成器等價(jià)于:
09
相同值的不可變對(duì)象
可以看到,key=1,value=’a’的鍵值對(duì)神奇地消失了。這里不得不說(shuō)一下Python字典是使用哈希表的思想實(shí)現(xiàn)的,Python 調(diào)用內(nèi)部的散列函數(shù),將鍵(Key)作為參數(shù)進(jìn)行轉(zhuǎn)換,得到一個(gè)唯一的地址,也就是哈希值。而Python 的哈希算法對(duì)相同的值計(jì)算得到的結(jié)果是一樣的,這就很好地解釋了上述情況出現(xiàn)的原因。
本文列出了在Python學(xué)習(xí)或者工作中可能會(huì)遇到的一些“坑”,雖然不見(jiàn)得每個(gè)人都能遇到上述問(wèn)題,但是可以作為一個(gè)參考,以后就能避免踩坑了。希望小伙伴在跟Python打交道的過(guò)程中能多注意細(xì)節(jié),甚至去了解一些內(nèi)部實(shí)現(xiàn)的原理,這樣才能更好地掌握Python這門(mén)語(yǔ)言。
總結(jié)
以上是生活随笔為你收集整理的python 获取第一个key_Python中常见的9大坑,看看你有没有遇到的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 生川芎的功效与作用、禁忌和食用方法
- 下一篇: fiddler设置中文版本_教你下载iO