Python 中那些令人防不勝防的坑

? ? ?作者:Rocky0429
? ? ?來(lái)源:Python空間

大家好,我是 Rocky0429,一個(gè)正在學(xué)習(xí) Python 的蒟蒻...
在學(xué)習(xí) Python 的過(guò)程中,我為它的簡(jiǎn)潔優(yōu)雅而癡迷,但它又是如此的調(diào)皮,在提供了很多舒服的功能特性之外,又悄悄挖了很多帶有迷惑性的坑,令人防不勝防...
人不能兩次踏入同一條河流,在無(wú)數(shù)次踩進(jìn)同樣的坑里之后,我覺(jué)得我有必要整理一下,一為自警,二為給大家提個(gè)醒,希望你不要和我犯相同的錯(cuò)誤。
這會(huì)是一個(gè)系列,每篇 5 個(gè),系列文章更新不定,不想錯(cuò)過(guò)的,記得點(diǎn)個(gè)關(guān)注,不迷路。

0x00 走丟的外部變量
首先我們先來(lái)看這么一個(gè)例子:
e =?429
try:
? ?raise?Exception()
except?Exception?as?e:
? ?pass
print(e)PS:except Exception as e?可以捕獲除了與程序退出(sys.exit())相關(guān)之外的所有異常。
在繼續(xù)向下看之前,你可以先思考一下上述例子可能出現(xiàn)的結(jié)果是什么,也可以自己嘗試著在編譯器里輸入一下。思考完了請(qǐng)繼續(xù)往下看。
出現(xiàn)的結(jié)果如下:
Traceback (most recent?call?last):
?File?"test.py", line?8,?in?<module>
? ?print(e)
NameError:?name?'e'?is?not?defined竟然報(bào)錯(cuò)了,那么這到底是為什么呢?
其實(shí)這是因?yàn)樵?Python3 中使用 as 分配異常的時(shí)候,在 except 的末尾將會(huì)把這個(gè)異常清除掉(在 Python2 中則不會(huì)出現(xiàn)這樣的情況)。這就好像將上面的示例變成下面的樣子:
e =?429
try:
? ?raise?Exception()
except?Exception?as?e:
? ?try:
? ? ? ?pass
? ?finally:
? ? ? ?del?e
print(e)通過(guò)上面的變形代碼,我們可以很清楚的看明白發(fā)生這一切的根源是什么:因?yàn)?e 被刪除了。這也變相的告訴我們,如果想要在 except 后面引用 e,必須將它先賦值給其它變量。
這樣看來(lái),是變量 e 執(zhí)行了 except 子句而被刪除,但是為什么 e 會(huì)去執(zhí)行 except 子句呢??jī)H僅是因?yàn)?e 和 as 后面的 e 長(zhǎng)的一毛一樣?
答案是否定的,其實(shí)這個(gè)是因?yàn)?/span>子句在 Python 中沒(méi)有獨(dú)立的作用域,所以上述示例中的所有內(nèi)容都處于同一個(gè)作用域里,所以變量 e 會(huì)因?yàn)閳?zhí)行了 except 子句而被刪除。
0x01?同樣是加,卻不一定等價(jià)
在我們來(lái)表示「加」這個(gè)概念的時(shí)候,一般我們會(huì)用兩種方式:a = a + b或者是 a += b 。在很多人的概念里這兩個(gè)其實(shí)就是一種,不分彼此,比如之前我就是這么認(rèn)為的,直到有一天有人拿著下面的坑過(guò)來(lái)讓我踩...
首先我們先來(lái)看第一個(gè)例子:
>>> a = [1,2,3]
>>> b = a
>>> a = a + [4,5,6]一個(gè)很簡(jiǎn)單的例子,你知道此時(shí)的 a 和 b 分別是多少么?請(qǐng)先自己思考一下再繼續(xù)向下看:
>>> a
[1, 2, 3, 4, 5, 6]
>>> b
[1, 2, 3]估計(jì)很多人都會(huì)答對(duì),表達(dá)式 a = a + [4,5,6] ,等號(hào)右邊其實(shí)是形成了一個(gè)新的列表,最后讓 a 引用了這個(gè)新的列表,而 b = a 引用的是之前的 a,所以 b 保持不變。
明白了上面的例子,我們接下來(lái)再看一個(gè)稍微有點(diǎn)區(qū)別的例子:
>>> a = [1,2,3]
>>> b = a
>>> a += [4,5,6]上面的例子和文章開(kāi)頭的例子區(qū)別在從 + 變成了 +=,按照我們慣性思維去想,肯定以為這倆例子就是一個(gè)東西的兩種不同寫法而已,可實(shí)際上真的是這樣嗎?讓我們來(lái)看一下此時(shí)的 a,b:
>>> a
[1, 2, 3, 4, 5, 6]
>>> b
[1, 2, 3, 4, 5, 6]咦?同樣是印象里的「加」,好像真的哪里有點(diǎn)不一樣誒。。
通過(guò)上面我們就可以看出 a = a + b 和 a += b 并不總是表現(xiàn)相同,起碼在列表上就是這么表現(xiàn)的。在這里的 a += [4,5,6] 實(shí)際上使用的是?extend?函數(shù),所以 a 和 b 仍然指向已被修改的同一列表。?
既然在這里說(shuō)到了 + 和 +=,索性再多補(bǔ)充一點(diǎn):在使用「加」的概念來(lái)連接字符串的時(shí)候,+= 其實(shí)比 + 的速度更快。?
下面我們來(lái)實(shí)際的演示一下用 + 連接三個(gè)字符串:
>>> import timeit
>>> timeit.timeit("a = a + b + c", setup="a='a'*10000;b='b'*10000;c='c'*10000",number=100)
0.07921688999340404
>>> timeit.timeit("a += b + c", setup="a='a'*10000;b='b'*10000;c='c'*10000",number=100)
0.002059974998701364上面的兩個(gè)結(jié)果很容易看出來(lái),在處理速度上,+= 比 + 處理的速度要快的多。出現(xiàn)這樣現(xiàn)象的原因是 += 執(zhí)行的是追加操作,追加操作的話就會(huì)比 + 少了一個(gè)銷毀然后新建的動(dòng)作,比如在 a += b + c 上 a 就不會(huì)被銷毀。
0x02?不一般的小括號(hào)
很多學(xué)過(guò)別的編程語(yǔ)言的同學(xué),很容易會(huì)忽略小括號(hào)?“()” 在 Python 中的一個(gè)重要表現(xiàn),那就是小括號(hào)還能表示“元組”?這一不可變數(shù)據(jù)類型。
>>> type(())
<class?'tuple'>
>>> tur = (1, 2)
>>> type(tur)
<class?'tuple'>但是如果小括號(hào)內(nèi)只有一個(gè)元素的話,比如像下面這樣,它就是小括號(hào)內(nèi)的元素類型:
>>> tur = (1)
>>> type(tur)
<class?'int'>>>> tur = (1, )
>>> type(tur)
<class?'tuple'>
0x03 列表的刪除沒(méi)有那么簡(jiǎn)單
假如我們有一個(gè)列表,我想刪除列表中的元素:
>>> lst = [1, 2, 3, 4, 5]
>>> for?i in?lst:
... lst.remove(i)>>> lst
[2, 4]
這是為啥子呢?是因?yàn)樵?for 循環(huán)中,如果我們刪除了 index =?0 (即 1)的值,原本 index = 1 ?及其之后的值會(huì)向前補(bǔ)位,所以當(dāng)前 index = 1 的值為之前 index = 2 的值。
列表的刪除操作我們經(jīng)常要用,所以大家要打起十二分的精神來(lái)對(duì)它。
0x04? is not 不分家
is not 在 Python 中是一伙的,用的時(shí)候要靠在一起,分開(kāi)以后就是兩個(gè)東西,結(jié)果會(huì)不一樣...
>>> [1, 2, 3] is?not?None
True
>>> [1, 2, 3] is?(not?None)
False看完有所收獲?點(diǎn)個(gè)在看,讓更多人可以看到~謝謝啦
今天的分享就到這,拜里個(gè)拜~
◆?◆?◆ ?◆?◆
長(zhǎng)按二維碼關(guān)注我們
數(shù)據(jù)森麟公眾號(hào)的交流群已經(jīng)建立,許多小伙伴已經(jīng)加入其中,感謝大家的支持。大家可以在群里交流關(guān)于數(shù)據(jù)分析&數(shù)據(jù)挖掘的相關(guān)內(nèi)容,還沒(méi)有加入的小伙伴可以掃描下方管理員二維碼,進(jìn)群前一定要關(guān)注公眾號(hào)奧,關(guān)注后讓管理員幫忙拉進(jìn)群,期待大家的加入。
管理員二維碼:
●?互聯(lián)網(wǎng)大佬學(xué)歷&背景大揭秘,看看是你的老鄉(xiāng)還是校友
