Python是一種解釋性、面向對象並具有動態語義的高級程序語言。它內建了高級的數據結構,結合了動態類型和動態綁定的優點,這使得它在快速應用開發中非常有吸引力,並且可作為腳本或膠水語言來連接現有的組件或服務。Python支持模塊和包,從而鼓勵了程序的模塊化和代碼重用。
Python簡單易學的語法可能會使Python開發者–尤其是那些編程的初學者–忽視了它的一些微妙的地方並低估了這門語言的能力。
有鑒於此,本文列出了一個“10強”名單,枚舉了甚至是高級Python開發人員有時也難以捕捉的錯誤。
《Python開發技術詳解》.( 周偉,宗傑).[高清PDF掃描版+隨書視頻+代碼] http://www.linuxidc.com/Linux/2013-11/92693.htm
Python腳本獲取Linux系統信息 http://www.linuxidc.com/Linux/2013-08/88531.htm
在Ubuntu下用Python搭建桌面算法交易研究環境 http://www.linuxidc.com/Linux/2013-11/92534.htm
Python允許為函數的參數提供默認的可選值。盡管這是語言的一大特色,但是它可能會導致一些易變默認值的混亂。例如,看一下這個Python函數的定義:
>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified
... bar.append("baz") # but this line could be problematic, as we'll see...
... return bar
一個常見的錯誤是認為在函數每次不提供可選參數調用時可選參數將設置為默認指定值。在上面的代碼中,例如,人們可能會希望反復(即不明確指定bar參數)地調用foo()時總返回'baz',由於每次foo()調用時都假定(不設定bar參數)bar被設置為[](即一個空列表)。
但是讓我們看一下這樣做時究竟會發生什麼:
>>> foo()
["baz"]>>> foo()
["baz", "baz"]>>> foo()
["baz", "baz", "baz"]
耶?為什麼每次foo()調用時都要把默認值"baz"追加到現有列表中而不是創建一個新的列表呢?
答案是函數參數的默認值只會評估使用一次—在函數定義的時候。因此,bar參數在初始化時為其默認值(即一個空列表),即foo()首次定義的時候,但當調用foo()時(即,不指定bar參數時)將繼續使用bar原本已經初始化的參數。
下面是一個常見的解決方法:
>>> def foo(bar=None):
... if bar is None: # or if not bar:
... bar = []
... bar.append("baz")
... return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz"]
常見錯誤 #2: 錯誤地使用類變量
考慮一下下面的例子:
>>> class A(object): ... x = 1 ... >>> class B(A): ... pass ... >>> class C(A): ... pass ... >>> print A.x, B.x, C.x 1 1 1
常規用一下。
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
嗯,再試一下也一樣。
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
什麼 $%#!&?? 我們只改了A.x,為什麼C.x也改了?
在Python中,類變量在內部當做字典來處理,其遵循常被引用的方法解析順序(MRO)。所以在上面的代碼中,由於class C中的x屬性沒有找到,它會向上找它的基類(盡管Python支持多重繼承,但上面的例子中只有A)。換句話說,class C中沒有它自己的x屬性,其獨立於A。因此,C.x事實上是A.x的引用。
假設你有如下一段代碼:
>>> try:
... l = ["a", "b"]
... int(l[2])
... except ValueError, IndexError: # To catch both exceptions, right?
... pass
...
Traceback (most recent call last):
File "<stdin>", line 3, in <module>
IndexError: list index out of range
這裡的問題在於 except 語句並不接受以這種方式指定的異常列表。相反,在Python 2.x中,使用語法 except Exception, e 是將一個異常對象綁定到第二個可選參數(在這個例子中是 e)上,以便在後面使用。所以,在上面這個例子中,IndexError 這個異常並不是被except語句捕捉到的,而是被綁定到一個名叫 IndexError的參數上時引發的。
在一個except語句中捕獲多個異常的正確做法是將第一個參數指定為一個含有所有要捕獲異常的元組。並且,為了代碼的可移植性,要使用as關鍵詞,因為Python 2 和Python 3都支持這種語法:
>>> try:
... l = ["a", "b"]
... int(l[2])
... except (ValueError, IndexError) as e:
... pass
...
>>>
Python是基於 LEGB 來進行作用於解析的, LEGB 是 Local, Enclosing, Global, Built-in 的縮寫。看起來“見文知意”,對嗎?實際上,在Python中還有一些需要注意的地方,先看下面一段代碼:
>>> x = 10
>>> def foo():
... x += 1
... print x
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
這裡出什麼問題了?
上面的問題之所以會發生是因為當你給作用域中的一個變量賦值時,Python 會自動的把它當做是當前作用域的局部變量,從而會隱藏外部作用域中的同名變量。
很多人會感到很吃驚,當他們給之前可以正常運行的代碼的函數體的某個地方添加了一句賦值語句之後就得到了一個 UnboundLocalError 的錯誤。 (你可以在這裡了解到更多)
尤其是當開發者使用 lists 時,這個問題就更加常見. 請看下面這個例子:
>>> lst = [1, 2, 3]
>>> def foo1():
... lst.append(5) # 沒有問題...
...
>>> foo1()
>>> lst
[1, 2, 3, 5]
>>> lst = [1, 2, 3]
>>> def foo2():
... lst += [5] # ... 但是這裡有問題!
...
>>> foo2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment
嗯?為什麼 foo2 報錯,而foo1沒有問題呢?
原因和之前那個例子的一樣,不過更加令人難以捉摸。foo1 沒有對 lst 進行賦值操作,而 foo2 做了。要知道, lst += [5] 是 lst = lst + [5] 的縮寫,我們試圖對 lst 進行賦值操作(Python把他當成了局部變量)。此外,我們對 lst 進行的賦值操作是基於 lst 自身(這再一次被Python當成了局部變量),但此時還未定義。因此出錯!
下面代碼中的問題應該是相當明顯的:
>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> for i in range(len(numbers)):
... if odd(numbers[i]):
... del numbers[i] # BAD: Deleting item from a list while iterating over it
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: list index out of range
當迭代的時候,從一個 列表 (List)或者數組中刪除元素,對於任何有經驗的開發者來說,這是一個眾所周知的錯誤。盡管上面的例子非常明顯,但是許多高級開發者在更復雜的代碼中也並非是故意而為之的。
幸運的是,Python包含大量簡潔優雅的編程范例,若使用得當,能大大簡化和精煉代碼。這樣的好處是能得到更簡化和更精簡的代碼,能更好的避免程序中出現當迭代時修改一個列表(List)這樣的bug。一個這樣的范例是遞推式列表(list comprehensions)。而且,遞推式列表(list comprehensions)針對這個問題是特別有用的,通過更改上文中的實現,得到一段極佳的代碼:
>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)] # ahh, the beauty of it all
>>> numbers
[0, 2, 4, 6, 8]
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-05/101668p2.htm