Python 中那些令人防不勝防的坑(一)

在這裏插入圖片描述

大家好,我是 Rocky0429,一個正在學習 Python 的蒟蒻…


在學習 Python 的過程中,我爲它的簡潔優雅而癡迷,但它又是如此的調皮,在提供了很多舒服的功能特性之外,又悄悄挖了很多帶有迷惑性的坑,令人防不勝防…


人不能兩次踏入同一條河流,在無數次踩進同樣的坑裏之後,我覺得我有必要整理一下,一爲自警,二爲給大家提個醒,希望你不要和我犯相同的錯誤。


這會是一個系列,每篇 5 個,系列文章更新不定,不想錯過的,記得點個關注,不迷路。



0x00 走丟的外部變量


首先我們先來看這麼一個例子:


e = 429

try:
   raise Exception()
except Exception as e:
   pass

print(e)

PS:except Exception as e 可以捕獲除了與程序退出(sys.exit())相關之外的所有異常。


在繼續向下看之前,你可以先思考一下上述例子可能出現的結果是什麼,也可以自己嘗試着在編譯器裏輸入一下。思考完了請繼續往下看。


出現的結果如下:


Traceback (most recent call last):
 File "test.py", line 8, in <module>
   print(e)
NameError: name 'e' is not defined

竟然報錯了,那麼這到底是爲什麼呢?

其實這是因爲在 Python3 中使用 as 分配異常的時候,在 except 的末尾將會把這個異常清除掉(在 Python2 中則不會出現這樣的情況)。這就好像將上面的示例變成下面的樣子:


e = 429

try:
   raise Exception()
except Exception as e:
   try:
       pass
   finally:
       del e

print(e)

通過上面的變形代碼,我們可以很清楚的看明白髮生這一切的根源是什麼:因爲 e 被刪除了。這也變相的告訴我們,如果想要在 except 後面引用 e,必須將它先賦值給其它變量。

這樣看來,是變量 e 執行了 except 子句而被刪除,但是爲什麼 e 會去執行 except 子句呢?僅僅是因爲 e 和 as 後面的 e 長的一毛一樣?


答案是否定的,其實這個是因爲子句在 Python 中沒有獨立的作用域,所以上述示例中的所有內容都處於同一個作用域裏,所以變量 e 會因爲執行了 except 子句而被刪除。



0x01 同樣是加,卻不一定等價


在我們來表示「加」這個概念的時候,一般我們會用兩種方式:a = a + b或者是 a += b 。在很多人的概念裏這兩個其實就是一種,不分彼此,比如之前我就是這麼認爲的,直到有一天有人拿着下面的坑過來讓我踩...

首先我們先來看第一個例子:


>>> a = [1,2,3]
>>> b = a
>>> a = a + [4,5,6]

一個很簡單的例子,你知道此時的 a 和 b 分別是多少麼?請先自己思考一下再繼續向下看:


>>> a
[1, 2, 3, 4, 5, 6]
>>> b
[1, 2, 3]

估計很多人都會答對,表達式 a = a + [4,5,6] ,等號右邊其實是形成了一個新的列表,最後讓 a 引用了這個新的列表,而 b = a 引用的是之前的 a,所以 b 保持不變。

明白了上面的例子,我們接下來再看一個稍微有點區別的例子:


>>> a = [1,2,3]
>>> b = a
>>> a += [4,5,6]

上面的例子和文章開頭的例子區別在從 + 變成了 +=,按照我們慣性思維去想,肯定以爲這倆例子就是一個東西的兩種不同寫法而已,可實際上真的是這樣嗎?讓我們來看一下此時的 a,b:
>>> a
[1, 2, 3, 4, 5, 6]
>>> b
[1, 2, 3, 4, 5, 6]

咦?同樣是印象裏的「加」,好像真的哪裏有點不一樣誒。。

通過上面我們就可以看出 a = a + b 和 a += b 並不總是表現相同,起碼在列表上就是這麼表現的。在這裏的 a += [4,5,6] 實際上使用的是 extend 函數,所以 a 和 b 仍然指向已被修改的同一列表。


既然在這裏說到了 + 和 +=,索性再多補充一點:在使用「加」的概念來連接字符串的時候,+= 其實比 + 的速度更快。


下面我們來實際的演示一下用 + 連接三個字符串:


>>> 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

上面的兩個結果很容易看出來,在處理速度上,+= 比 + 處理的速度要快的多。出現這樣現象的原因是 += 執行的是追加操作,追加操作的話就會比 + 少了一個銷燬然後新建的動作,比如在 a += b + c 上 a 就不會被銷燬。

0x02 不一般的小括號


很多學過別的編程語言的同學,很容易會忽略小括號 “()” 在 Python 中的一個重要表現,那就是小括號還能表示“元組” 這一不可變數據類型。


>>> type(())
<class 'tuple'>
>>> tur = (1, 2)
>>> type(tur)
<class 'tuple'>

但是如果小括號內只有一個元素的話,比如像下面這樣,它就是小括號內的元素類型:
>>> tur = (1)
>>> type(tur)
<class 'int'>

天吶個天...

那麼如果想要表示一個元素的元組咋整呢?要像下面這樣:


>>> tur = (1, )
>>> type(tur)
<class 'tuple'>

加個逗號 “,” 就 ok 啦...

0x03 列表的刪除沒有那麼簡單


假如我們有一個列表,我想刪除列表中的元素:
>>> lst = [1, 2, 3, 4, 5]
>>> for i in lst:
... lst.remove(i)

在剛開始學習 Python 之初,這是很多同學容易想到的做法,然而我們來看一下程序運行完以後的結果:
>>> lst
[2, 4]

看到這個結果,我...

在這裏插入圖片描述


這是爲啥子呢?是因爲在 for 循環中,如果我們刪除了 index = 0 (即 1)的值,原本 index = 1 及其之後的值會向前補位,所以當前 index = 1 的值爲之前 index = 2 的值。


列表的刪除操作我們經常要用,所以大家要打起十二分的精神來對它。



0x04 is not 不分家


is not 在 Python 中是一夥的,用的時候要靠在一起,分開以後就是兩個東西,結果會不一樣…


>>> [1, 2, 3] is not None
True
>>> [1, 2, 3] is (not None)
False

這就在於,is not 是個單獨的二元運算符,當它兩側的變量指向同一個對象的時候,結果爲 False,否則結果爲 True,希望引起大家的注意...

❤️ 看完有所收穫?希望愛學習的你不要吝嗇三連擊喲[點贊 + 收藏 + 評論]~


另外本蒟蒻把公衆號的高分原創文章整理成了一本電子書,取名《Python修煉之道》,一共 400 頁!

具體內容請戳:熬夜爆肝整理 400 頁 《Python 修煉之道》,一本高分原創高清電子書送給你!

目錄如下:


在這裏插入圖片描述

現在免費送給大家,在我的公衆號Python空間(微信搜 Devtogether) 回覆 修煉之道即可獲取。


作者Info:

【作者】:Rocky0429
【原創公衆號】:Python空間。
【簡介】:CSDN 博客專家, 985 計算機在讀研究生,ACM 退役狗 & 亞洲區域賽銀獎划水選手。這是一個堅持原創的技術公衆號,專注Python 編程,每天堅持推送各種 Python 基礎/進階文章,數據分析,爬蟲實戰,數據結構與算法,不定期分享各類資源。
【轉載說明】:轉載請說明出處,謝謝合作!~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章