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

前言
文的文字及圖片來源於網絡,僅供學習、交流使用,不具備任何商業用途,版權歸原做者全部,若有問題請及時聯繫咱們以做處理。python

做者: Rocky0429編程

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

人不能兩次踏入同一條河流,在無數次踩進一樣的坑裏以後,我以爲我有必要整理一下,一爲自警,二爲給你們提個醒,但願你不要和我犯相同的錯誤。
網絡

0x00 走丟的外部變量

首先咱們先來看這麼一個例子:編程語言


e = 429

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

print(e)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

PS:except Exception as e 能夠捕獲除了與程序退出(sys.exit())相關以外的全部異常。函數

PS:若是你缺少最新python實戰教程,能夠去小編的Python技術.裙 :一久五其二二流一思(數字的諧音)轉換下能夠找到了學習

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

出現的結果以下:atom


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

居然報錯了,那麼這究竟是爲何呢? 

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


e = 429

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

print(e)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

經過上面的變形代碼,咱們能夠很清楚的看明白髮生這一切的根源是什麼:由於 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]
  • 1
  • 2
  • 3

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


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

估計不少人都會答對,表達式 a = a + [4,5,6] ,等號右邊實際上是造成了一個新的列表,最後讓 a 引用了這個新的列表,而 b = a 引用的是以前的 a,因此 b 保持不變。 

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


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

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

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

經過上面咱們就能夠看出 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
  • 1
  • 2
  • 3
  • 4
  • 5

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

0x02 不通常的小括號

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


>>> type(())
<class 'tuple'>
>>> tur = (1, 2)
>>> type(tur)
<class 'tuple'>
  • 1
  • 2
  • 3
  • 4
  • 5

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

天吶個天... 

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


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

加個逗號 「,」 就 ok 啦... 

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

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

在剛開始學習 Python 之初,這是不少同窗容易想到的作法,然而咱們來看一下程序運行完之後的結果: 
>>> lst
[2, 4]
  • 1
  • 2

看到這個結果,我...

在這裏插入圖片描述


這是爲啥子呢?是由於在 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
 
  • 1
  • 2
  • 3
  • 4
這就在於,is not 是個單獨的二元運算符,當它兩側的變量指向同一個對象的時候,結果爲 False,不然結果爲 True,但願引發你們的注意.
相關文章
相關標籤/搜索