當然,一個常見的解決辦法就是:
>>> 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
請看下面這段代碼:
>>> try:
... l = ["a", "b"]
... int(l[2])
... except ValueError, IndexError: # To catch both exceptions, right?
... pass
...
Traceback (most recent call last):
File "<stdin>"</stdin>, line 3, in <module>
IndexError: list index out of range
Python中的變數名解析遵循所謂的LEGB原則,也就是“L:本地範圍;E:上一層結構中def或lambda的本地範圍;G:全域範圍;B:內建範圍”(Local,Enclosing,Global,Builtin),按順序尋找。看上去是不是很簡單?不過,事實上這個原則的生效方式還是有著一些特殊之處。說到這點,我們就不得不提下面這個常見的Python編程錯誤。請看下面的代碼:
>>> x = 10
>>> def foo():
... x += 1
... print x
...
>>> foo()
Traceback (most recent call last):
File "<stdin>"</stdin>, line 1, in <module>
File "<stdin>"</stdin>, line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment
下面這段代碼的問題應該算是十分明顯:
>>> 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>"</stdin>, line 2, in <module>
IndexError: list index out of range
幸運的是,Python語言融合了許多優雅的編程範式,如果使用得當,可以極大地簡化代碼。簡化代碼還有一個好處,就是不容易出現在遍曆列表時刪除元素這個錯誤。能夠做到這點的一個編程範式就是列表解析式。而且,列表解析式在避免這個問題方面尤其有用,下面用列表解析式重新實現上面代碼的功能:
>>> 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]
常見錯誤6:不理解Python在閉包中如何綁定變數
請看下面這段代碼:
>>> def create_multipliers():
... return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
... print multiplier(2)
...
要解決這個常見Python問題的方法中,需要使用一些hack技巧:
>>> def create_multipliers():
... return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
... print multiplier(2)
...
0
2
4
6
8
但是,如果我們試著匯入b.py模組呢(即之前沒有引用a.py模組的前提下):
>>> import b
Traceback (most recent call last):
File "<stdin>"</stdin>, line 1, in <module>
File "b.py", line 1, in <module>
import a
File "a.py", line 6, in <module>
print f()
File "a.py", line 4, in f
return b.x
AttributeError: 'module' object has no attribute 'x'
解決這個問題有一種非常簡單的方法,就是簡單地修改下b.py模組,在g()函數內部才引用a.py:
x = 1
def g():
import a # This will be evaluated only when g() is called
print a.f()
現在我們再匯入b.py模組的話,就不會出現任何問題了:
>>> import b
>>> b.g()
1 # Printed a first time since module 'a' calls 'print f()' at the end
1 # Printed a second time, this one is our call to 'g'
常見錯誤8:模組命名與Python標準庫模組名衝突
但是現在,我們換成Python 3再運行一遍:
$ python3 foo.py 1
key error
Traceback (most recent call last):
File "foo.py", line 19, in <module>
bad()
File "foo.py", line 17, in bad
print(e)
UnboundLocalError: local variable 'e' referenced before assignment