Python開發(fā)人員最常見的8個錯誤
大多數(shù)python開發(fā)人員具有不同的核心編程語言背景,例如java,C?;騝 ++。 因此,他們習(xí)慣于用艱苦的方式做事,而當(dāng)它們以簡單易學(xué)的Python語言被引入時,它們會誤解Python的多樣性和功能,并常常最終導(dǎo)致自己誤導(dǎo)其失去某些細(xì)微之處。
在本文中,我將嘗試解決Python程序員遇到的錯誤。 這些錯誤甚至是本文中的大多數(shù)錯誤都是針對中級甚至專家級的開發(fā)人員的。 想知道? 如果您是初學(xué)者或中級開發(fā)人員,請繼續(xù)閱讀文章Python開發(fā)人員最常犯的10個錯誤,因為當(dāng)前文章適合更高級的讀者。

1.遍歷列表時修改列表
這是每個Python開發(fā)人員一生中至少面對一次的問題。在下面的代碼片段中查看問題:
- >>> 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
這個問題非常明顯,但即使是高級開發(fā)人員,在復(fù)雜的工作流程中添加代碼時也會犯類似的錯誤。
有幾種解決方案。我想在這里討論一個最佳解決方案,但據(jù)我說這是最簡單的解決方案,因此我不太可能產(chǎn)生錯誤。我建議列表理解在這種情況下非常有用。看看上面的代碼具有列表理解的實現(xiàn):
- >>> 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]
2.創(chuàng)建循環(huán)模塊依賴項
假設(shè)您有兩個文件a.py和b.py,每個文件都導(dǎo)入另一個文件,如下所示:
在a.py中:
- import b
- def f(): return b.x
- print f()
在b.py中:
- import a
- x = 1
- def g():
- print a.f()
首先,讓我們嘗試導(dǎo)入a.py:
- >>> import a
- 1
一切正常,沒有錯誤。它應(yīng)該給您一個錯誤,但是這里的問題是,如果存在循環(huán)依賴關(guān)系,您有時可以擺脫它,因為python足夠聰明,可以跟蹤導(dǎo)入的軟件包。當(dāng)每個模塊嘗試訪問另一個模塊的功能時會發(fā)生問題,因為不會聲明另一個模塊,這將導(dǎo)致AttributeError,如下所示:
- >>> import b
- Traceback (most recent call last): File "<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'
要解決此問題,我們需要在函數(shù)內(nèi)部導(dǎo)入其他依賴模塊:
- x = 1
- def g(): import a # 僅在調(diào)用g()時才會計算
- print a.f()
現(xiàn)在一切都應(yīng)該運行良好:
- >>> import b
- >>> b.g()1 #自模塊'a'在最后調(diào)用'print f()'以來首次打印
- 1 #第二次打印,這是我們對“ g”的調(diào)用
3.錯誤使用表達(dá)式作為函數(shù)參數(shù)的默認(rèn)值
這是很難調(diào)試的錯誤之一,因為它不會給您帶來錯誤,而且在大多數(shù)情況下它可以正常工作,并且開發(fā)人員可以擺脫它。當(dāng)我們可以指定一個可變的可選函數(shù)參數(shù)時,就會發(fā)生這種情況。例如:
- >>> def foo(bar=[]):
- ... bar.append("baz")
- ... return bar
看起來我們已經(jīng)創(chuàng)建了一個函數(shù),該函數(shù)會將baz附加在指定給它的列表的末尾,否則每次在不使用bar參數(shù)的情況下調(diào)用它都將返回[“ baz”],因為bar將被初始化為[]。但是,當(dāng)我們執(zhí)行它時,我們得到以下結(jié)果:
- >>> foo()
- ["baz"]
- >>> foo()
- ["baz", "baz"]
- >>> foo()
- ["baz", "baz", "baz"]
輸出結(jié)果并非預(yù)期的那樣,現(xiàn)在您將看到,如果沒有人注意到此問題,那么如果調(diào)用它,大多數(shù)開發(fā)人員將如何擺脫它。通過查看代碼,很難找到尚未遇到此問題或不知道Python如何評估函數(shù)的人,因為每次它將bar的值初始化為[]時,都很難。但是在Python中,默認(rèn)參數(shù)僅被評估一次,因此在第一次調(diào)用時它會按預(yù)期工作,但是在第二次和第三次調(diào)用中,它使用現(xiàn)有的條形列表而不是對其進行初始化。她是我們?nèi)绾谓鉀Q這個問題的方法:
- >>> def foo(bar=None):
- ... if bar is None: #或者 if not bar:
- ... bar = []
- ... bar.append("baz")
- ... return bar
- ...>>> foo()["baz"]
- >>> foo()["baz"]
- >>> foo()["baz"]
4.錯誤使用類變量
考慮以下示例:
- >>> 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
再次如預(yù)期
- >>> A.x = 3
- >>> print A.x, B.x, C.x
- 3 2 3
什么?通過更改A類的值,C類不會受到影響是很奇怪的。這是因為在Python中,類變量在內(nèi)部作為字典處理,并且遵循稱為“方法解析順序”的順序。發(fā)生這種情況是因為在類C中找不到屬性x,所以在基類A中對其進行了查找。
5.為異常塊指定不正確的參數(shù)
對于給定的代碼:
- >>> 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
- 追溯(最近一次通話): <模塊>中第3行的文件“ <stdin>”
- IndexError:列表索引超出范圍
看來您正在嘗試捕獲這兩個異常,但這是行不通的。此錯誤通常是由來自python 2.x背景的開發(fā)人員犯的,因為在Python 2中,此語法用于將異常綁定到可選參數(shù)。我們的代碼應(yīng)該捕獲了IndexError異常。正確的方法是使用元組,方法是指定要在元組中捕獲并使用as綁定到參數(shù)的所有異常。 python 2&3也支持此語法:
- >>> try:
- ... l = ["a", "b"]
- ... int(l[2])
- ... except (ValueError, IndexError) as e:
- ... pass
- ...>>>
6.對Python作用域規(guī)則的誤解
Python范圍解析基于所謂的LEGB規(guī)則,它是Local,Enclosing,Global,Built-in的簡寫。但這會使開發(fā)人員遇到麻煩,例如:
- >>> 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
- 追溯(最近一次通話):
- <模塊>中第1行的文件“ <stdin>”
- 文件“ <stdin>”,第2行,在foo中
- UnboundLocalError:分配前已引用局部變量“ x”
沒道理我們已經(jīng)聲明x應(yīng)該可以正常運行。調(diào)用該函數(shù)會在這里尋找變量x,但找不到變量x,它將把它帶到外部作用域。直到我們對其進行分配之前,它都可以正常工作。我們得到UnboundLocalError以避免函數(shù)意外更改變量的值。有關(guān)更多信息,請參見此處。
為了進一步說明,下面是一些其他示例:
- >>> 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
- 追溯(最近一次通話):
- <模塊>中第1行的文件“ <stdin>”
- 文件“ <stdin>”,行2,在foo中
- UnboundLocalError:分配前已引用局部變量“ lst”
在這兩種情況下,我們都嘗試從外部范圍更新列表。在第一個示例中它起作用了,但是在第二個示例中卻沒有起作用,因為我們在函數(shù)主體中的該列表上使用了賦值運算符。這將嘗試將計算/評估的值存儲到foo2中不存在的局部變量lst中。
7.混淆Python如何在閉包中綁定變量
考慮以下示例:、
- >>> def create_multipliers():
- ... return [lambda x : i * x for i in range(5)]
- >>> for multiplier in create_multipliers():
- ... print multiplier(2)
- ...
您可能期望以下輸出:
- 0
- 2
- 4
- 6
- 8
但是您實際上得到:
- 8
- 8
- 8
- 8
- 8
由于Python的后期綁定行為,即在調(diào)用內(nèi)部函數(shù)時會搜索閉包中使用的變量的值。因此,在上面的代碼中,無論何時調(diào)用任何返回的函數(shù),i的值都會在調(diào)用時在周圍的范圍內(nèi)進行搜索,但是到發(fā)生這種情況時,循環(huán)就完成了,因此我已經(jīng)為其分配了i最終值為4。一種解決方法是:
- >>> 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
8.重新發(fā)明輪子
這是在來自低級語言背景(例如c ++,c#等)的開發(fā)人員中最常見的。由于龐大的社區(qū)和大量的開放源代碼內(nèi)容以及對python社區(qū)的幫助,如果沒有現(xiàn)有的解決方案,很難發(fā)現(xiàn)問題。但是在這里,我談?wù)摰氖侵匦聞?chuàng)建python提供的基礎(chǔ)。其中一些可能包括使用裝飾器,生成器,內(nèi)置函數(shù)。我最好的例子是排序功能。當(dāng)我為開發(fā)人員編寫滿足其獨特需求的自定義排序函數(shù)時,我已經(jīng)看到了好幾次。在所有情況下,整個功能都可以用更簡單,更優(yōu)雅和更強大的代碼代替:
- list.sort(key=…, reverse=…)
很難找到無法解決我們問題的實際方案。我們甚至可以使用此方法對元組,字典或任何Python對象進行排序。
看下面的例子:
- >>> vowels = ['e', 'a', 'u', 'o', 'i']
- >>> vowels.sort(reverse=True)
- >>> print('Sorted list (in Descending):', vowels)
- Sorted list (in Descending): ['u', 'o', 'i', 'e', 'a']
這是元組列表的示例
- # take second element for sort#將第二個元素進行排序
- def takeSecond(elem): return elem[1]
- # random list#隨機列表
- random = [(2, 2), (3, 4), (4, 1), (1, 3)]
- # sort list with key#用鍵排序列表
- random.sort(key=takeSecond)
- # print list#打印清單
- print('Sorted list:', random)
- Output: Sorted list: [(4, 1), (2, 2), (1, 3), (3, 4)]
總結(jié):
即使python很容易上手,但對所使用的工具或范例缺乏深入的了解也會使您陷入麻煩。我希望你們中的一些人會發(fā)現(xiàn)