Python編程中容易出現的10個錯誤

Python是一個解釋型、面向對象、具有動態語義的高級語言。它具有高級數據結構、動態類型綁定,支持模塊化。由於Python程序員往往並非只會這一門語言,受其他語言的影響,我們在編寫Python代碼的時候容易出現一些錯誤,本文中列出了10點。

一、函數參數默認值
Python允許定義函數參數的默認值,這和C/C++是一致的。但是所不同的是,解釋器只對該默認參數賦值一次。

>>> def foo(bar=[]):
...    bar.append("baz”)    
...    return bar

比如說這個foo函數,如果調用的時候自帶有參數foo(a),這沒什麼可說的。如果用foo(),那麼bar只有在一次調用無參函數foo()的時候纔會執行bar=[],之後若再調用foo(),那麼bar會繼續使用剛纔的bar,這個bar就類似於C中的static局部變量。

>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz”]


解決辦法是,在函數內部做判斷。如果默認參數想定義爲空的類型,用None。

>>> def foo(bar=None):
...    if bar is None:# or if not bar:
...        bar = []
...    bar.append("baz")
...    return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz”]


二、類變量的繼承
Python裏,類變量的內部是由字典實現的。在派生類變量中,如果成員沒有被定義,則會去基類中找。如果有的話的就不去看基類的了。

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

三、異常處理時參數聲明
異常處理時except後面不能跟多個異常。
>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except ValueError, IndexError: 
...     pass

上面這麼寫是不對的,第二個異常情況是捕獲不到的,應該像下面這樣寫。

>>> try:
...     l = ["a", "b"]
...     int(l[2])
... except (ValueError, IndexError) as e:  
...     pass



四、變量的作用域
C/C++裏,全局變量在函數內部是完全可見的。但是在Python裏,由於是解釋型語言,所以不一樣了。對於一個變量,如果首次出現在賦值語句的左邊,那麼會被認爲是一個局部變量,和外面的同名全局變量沒關係。如果首次出現在賦值語句右邊或者其他地方,那麼就會被認爲是全局變量,直接用全局變量。

>>> lst = [1, 2, 3]
>>> def foo1():
...     lst.append(5)   # This works ok...
...
>>> foo1()
>>> lst
[1, 2, 3, 5]

>>> lst = [1, 2, 3]
>>> def foo2():
...     lst += [5]      # ... but this bombs!
...
>>> foo2()


這就是爲什麼上面兩個函數,前者是正確的,後者是錯誤的。

五、在遍歷list的時候修改它
這個錯誤其實在C++使用迭代器時也會出現。即使用for循環來遍歷list時,由於循環之前就確定了循環次數,修改list會導致下標的變化。

>>> 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]
...
Traceback (most recent call last):
    File "<stdin>", line 2, in <module>
IndexError: list index out of range

使用下面這種高級寫法,可以避免此類錯誤。

>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)]
>>> numbers
[0, 2, 4, 6, 8]


六、閉包中綁定變量
閉包中的變量的值是內函數lambda x : i * x 調用的時候才確定的。

>>> def create_multipliers():
...     return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
...     print multiplier(2)


對於上面的程序,不管哪一個內函數被調用時,i的值都是最後的4。因此輸出會是8 8 8 8 8,而不是0 2 4 6 8。解決的辦法可以利用默認參數來生成匿名函數。

>>> def create_multipliers():
...     return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
...     print multiplier(2)


七、模塊之間的循環依賴
Python解釋器會去設法避免重複導入模塊,但是要注意順序。


八、小心重名的模塊
Python的類庫非常豐富,但是要小心重名的模塊。

九、不同Python版本的坑
注意Python2和Python3的區別。

十、類的析構函數
當析構函數被調用時,解釋器會把變量變爲None,這樣有些事情就做不了了。

import foo
class Bar(object):
       ...
    def __del__(self):
        foo.cleanup(self.myhandle)

可以使用atexit.register來引入另一個函數做想做的事情。

import foo
import atexit
def cleanup(handle):
    foo.cleanup(handle)

class Bar(object):
    def __init__(self):
        ...
        atexit.register(cleanup, self.myhandle)

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