亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Python > 正文

十個Python程序員易犯的錯誤

2020-01-04 17:54:32
字體:
來源:轉載
供稿:網友
不管是在學習還是工作過程中,人都會犯錯。雖然Python的語法簡單、靈活,但也一樣存在一些不小的坑,一不小心,初學者和資深Python程序員都有可能會栽跟頭。本文為大家分享了10大常見錯誤,需要的朋友可以參考下
 

常見錯誤1:錯誤地將表達式作為函數的默認參數

在Python中,我們可以為函數的某個參數設置默認值,使該參數成為可選參數。雖然這是一個很好的語言特性,但是當默認值是可變類型時,也會導致一些令人困惑的情況。我們來看看下面這個Python函數定義:

>>> def foo(bar=[]):    # bar是可選參數,如果沒有提供bar的值,則默認為[],...  bar.append("baz")  # 但是稍后我們會看到這行代碼會出現問題。...  return bar

Python程序員常犯的一個錯誤,就是想當然地認為:在每次調用函數時,如果沒有為可選參數傳入值,那么這個可選參數就會被設置為指定的默認值。在上面的代碼中,你們可能覺得重復調用foo()函數應該會一直返回'baz',因為你們默認每次foo()函數執行時(沒有指定bar變量的值),bar變量都被設置為[](也就是,一個新的空列表)。

但是,實際運行結果卻是這樣的:

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

很奇怪吧?為什么每次調用foo()函數時,都會把"baz"這個默認值添加到已有的列表中,而不是重新創建一個新的空列表呢?

答案就是,可選參數默認值的設置在Python中只會被執行一次,也就是定義該函數的時候。因此,只有當foo()函數被定義時,bar參數才會被初始化為默認值(也就是,一個空列表),但是之后每次foo()函數被調用時,都會繼續使用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.x1 1 1

這個結果很正常。

>>> B.x = 2>>> print A.x, B.x, C.x1 2 1

嗯,結果和預計的一樣。

>>> A.x = 3>>> print A.x, B.x, C.x3 2 3

在Python語言中,類變量是以字典的形式進行處理的,并且遵循方法解析順序(Method Resolution Order,MRO)。因此,在上面的代碼中,由于類C中并沒有x這個屬性,解釋器將會查找它的基類(base class,盡管Python支持多重繼承,但是在這個例子中,C的基類只有A)。換句話說,C并不沒有獨立于A、真正屬于自己的x屬性。所以,引用C.x實際上就是引用了A.x。如果沒有處理好這里的關系,就會導致示例中出現的這個問題。

常見錯誤3:錯誤地指定異常代碼塊(exception block)的參數

請看下面這段代碼:

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

這段代碼的問題在于,except語句并不支持以這種方式指定異常。在Python 2.x中,需要使用變量e將異常綁定至可選的第二個參數中,才能進一步查看異常的情況。因此,在上述代碼中,except語句并沒有捕獲IndexError異常;而是將出現的異常綁定到了一個名為IndexError的參數中。

要想在except語句中正確地捕獲多個異常,則應將第一個參數指定為元組,然后在元組中寫下希望捕獲的異常類型。另外,為了提高可移植性,請使用as關鍵詞,Python 2和Python 3均支持這種用法。

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

常見錯誤4:錯誤理解Python中的變量名解析

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 fooUnboundLocalError: local variable 'x' referenced before assignment

出了什么問題?

上述錯誤的出現,是因為當你在某個作用域內為變量賦值時,該變量被Python解釋器自動視作該作用域的本地變量,并會取代任何上一層作用域中相同名稱的變量。

正是因為這樣,才會出現一開始好好的代碼,在某個函數內部添加了一個賦值語句之后卻出現了UnboundLocalError,難怪會讓許多人吃驚。

在使用列表時,Python程序員尤其容易陷入這個圈套。

請看下面這個代碼示例:

>>> 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>"</stdin>, line 1, in <module> File "<stdin>"</stdin>, line 2, in fooUnboundLocalError: local variable 'lst' referenced before assignment

呃?為什么函數foo1運行正常,foo2卻出現了錯誤?

答案與上一個示例相同,但是卻更難捉摸清楚。foo1函數并沒有為lst變量進行賦值,但是foo2卻有賦值。我們知道,lst += [5]只是lst = lst + [5]的簡寫,從中我們就可以看出,foo2函數在嘗試為lst賦值(因此,被Python解釋器認為是函數本地作用域的變量)。但是,我們希望為lst賦的值卻又是基于lst變量本身(這時,也被認為是函數本地作用域內的變量),也就是說該變量還沒有被定義。這才出現了錯誤。

常見錯誤5:在遍歷列表時更改列表

下面這段代碼的問題應該算是十分明顯:

>>> 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開發人員都會注意的問題。但是盡管上面的示例十分明顯,資深開發人員在編寫更為復雜代碼的時候,也很可能會無意之下犯同樣的錯誤。

幸運的是,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中的遲綁定(late binding )機制,即閉包中變量的值只有在內部函數被調用時才會進行查詢。因此,在上面的代碼中,每次create_multipliers()所返回的函數被調用時,都會在附近的作用域中查詢變量i的值(而到那時,循環已經結束,所以變量i最后被賦予的值為4)。

要解決這個常見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)...02468

請注意!我們在這里利用了默認參數來實現這個lambda匿名函數。有人可能認為這樣做很優雅,有人會覺得很巧妙,還有人會嗤之以鼻。但是,如果你是一名Python程序員,不管怎樣你都應該要了解這種解決方法。

常見錯誤7:模塊之間出現循環依賴(circular dependencies)

假設你有兩個文件,分別是a.py和b.py,二者相互引用,如下所示:

a.py文件中的代碼:

import bdef f():  return b.xprint f()b.py文件中的代碼:import ax = 1def g():  print a.f()

首先,我們嘗試導入a.py模塊:

代碼運行正常。也許這出乎了你的意料。畢竟,我們這里存在循環引用這個問題,想必應該是會出現問題的,難道不是嗎?

答案是,僅僅存在循環引用的情況本身并不會導致問題。如果一個模塊已經被引用了,Python可以做到不再次進行引用。但是如果每個模塊試圖訪問其他模塊定義的函數或變量的時機不對,那么你就很可能陷入困境。

那么回到我們的示例,當我們導入a.py模塊時,它在引用b.py模塊時是不會出現問題的,因為b.py模塊在被引用時,并不需要訪問在a.py模塊中定義的任何變量或函數。b.py模塊中對a模塊唯一的引用,就是調用了a模塊的foo()函數。但是那個函數調用發生在g()函數當中,而a.py或b.py模塊中都沒有調用g()函數。所以,不會出現問題。

但是,如果我們試著導入b.py模塊呢(即之前沒有引用a.py模塊的前提下):

>>> import bTraceback (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.xAttributeError: 'module' object has no attribute 'x'

糟糕。情況不太妙!這里的問題是,在導入b.py的過程中,它試圖引用a.py模塊,而a.py模塊接著又要調用foo()函數,這個foo()函數接著又試圖去訪問b.x變量。但是這個時候,b.x變量還沒有被定義,所以才出現了AttributeError異常。

解決這個問題有一種非常簡單的方法,就是簡單地修改下b.py模塊,在g()函數內部才引用a.py:

x = 1def 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 end1 # Printed a second time, this one is our call to 'g'

常見錯誤8:模塊命名與Python標準庫模塊名沖突

Python語言的一大優勢,就是其本身自帶的強大標準庫。但是,正因為如此,如果你不去刻意注意的話,你也是有可能為自己的模塊取一個和Python自帶標準庫模塊相同的名字(例如,如果你的代碼中有一個模塊叫email.py,那么這就會與Python標準庫中同名的模塊相沖突。)

這很可能會給你帶來難纏的問題。舉個例子,在導入模塊A的時候,假如該模塊A試圖引用Python標準庫中的模塊B,但卻因為你已經有了一個同名模塊B,模塊A會錯誤地引用你自己代碼中的模塊B,而不是Python標準庫中的模塊B。這也是導致一些嚴重錯誤的原因。

因此,Python程序員要格外注意,避免使用與Python標準庫模塊相同的名稱。畢竟,修改自己模塊的名稱比提出PEP提議修改上游模塊名稱且讓提議通過,要來得容易的多。

常見錯誤9:未能解決Python 2與Python 3之間的差異

假設有下面這段代碼:

import sysdef bar(i):  if i == 1:    raise KeyError(1)  if i == 2:    raise ValueError(2)def bad():  e = None  try:    bar(int(sys.argv[1]))  except KeyError as e:    print('key error')  except ValueError as e:    print('value error')  print(e)bad()

如果是Python 2,那么代碼運行正常:

$ python foo.py 1key error1$ python foo.py 2value error2

但是現在,我們換成Python 3再運行一遍:

$ python3 foo.py 1key errorTraceback (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

這到底是怎么回事?這里的“問題”是,在Python 3中,異常對象在except代碼塊作用域之外是無法訪問的。(這么設計的原因在于,如果不這樣的話,堆棧幀中就會一直保留它的引用循環,直到垃圾回收器運行,將引用從內存中清除。)

避免這個問題的一種方法,就是在except代碼塊的作用域之外,維持一個對異常對象的引用(reference),這樣異常對象就可以訪問了。下面這段代碼就使用了這種方法,因此在Python 2和Python 3中的輸出結果是一致的:

import sysdef bar(i):  if i == 1:    raise KeyError(1)  if i == 2:    raise ValueError(2)def good():  exception = None  try:    bar(int(sys.argv[1]))  except KeyError as e:    exception = e    print('key error')  except ValueError as e:    exception = e    print('value error')  print(exception)good()

在Python 3下運行代碼:

$ python3 foo.py 1key error1$ python3 foo.py 2value error2

太棒了!

常見錯誤10:錯誤使用del方法

假設你在mod.py的文件中編寫了下面的代碼:

import fooclass Bar(object):    ...  def __del__(self):    foo.cleanup(self.myhandle)之后,你在another_mod.py文件中進行如下操作:import modmybar = mod.Bar()

如果你運行another_mod.py模塊的話,將會出現AttributeError異常。

為什么?因為當解釋器結束運行的時候,該模塊的全局變量都會被設置為None。因此,在上述示例中,當__del__方法被調用之前,foo已經被設置成了None。

要想解決這個有點棘手的Python編程問題,其中一個辦法就是使用atexit.register()方法。這樣的話,當你的程序執行完成之后(即正常退出程序的情況下),你所指定的處理程序就會在解釋器關閉之前運行。

應用了上面這種方法,修改后的mod.py文件可能會是這樣子的:

import fooimport atexitdef cleanup(handle):  foo.cleanup(handle)class Bar(object):  def __init__(self):    ...    atexit.register(cleanup, self.myhandle)

這種實現支持在程序正常終止時干凈利落地調用任何必要的清理功能。很明顯,上述示例中將會由foo.cleanup函數來決定如何處理self.myhandle所綁定的對象。

綜述

Python是一門強大而又靈活的編程語言,提供的許多編程機制和范式可以極大地提高工作效率。但是與任何軟件工具或語言一樣,如果對該語言的能力理解有限或無法欣賞,那么有時候自己反而會被阻礙,而不是受益了。正如一句諺語所說,“自以為知道夠多,但實則會給自己或別人帶來危險。

不斷地熟悉Python語言的一些細微之處,尤其是本文中提到的10大常見錯誤,將會幫助你有效地使用這門語言,同時也能避免犯一些比較常見的錯誤。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩av三级在线观看| 日本精品视频在线观看| 日韩中文在线中文网三级| 精品国产户外野外| 亚洲欧美国产精品专区久久| 国产精品99久久久久久久久久久久| 国产精品成人在线| 国产一区二区日韩| 国产精品丝袜白浆摸在线| 国产精品亚洲аv天堂网| 国产精品日日做人人爱| 国外成人在线直播| 超碰97人人做人人爱少妇| 国产精品电影在线观看| 欧美日韩亚洲网| 91精品免费视频| 成人h视频在线观看播放| 曰本色欧美视频在线| 97久久精品在线| 97热精品视频官网| 欧美色道久久88综合亚洲精品| 日韩精品亚洲视频| 欧美激情xxxx| 亚洲欧美日韩综合| 国产精品久久久久久亚洲调教| 欧美有码在线观看| 在线成人激情黄色| 色播久久人人爽人人爽人人片视av| 国产美女直播视频一区| 日本亚洲欧洲色| 亚洲欧洲美洲在线综合| 精品精品国产国产自在线| 亚洲第一av网站| 欧美激情图片区| 国产成人福利夜色影视| 中文字幕亚洲欧美一区二区三区| 久久久国产精品一区| 久久久久这里只有精品| 美女视频黄免费的亚洲男人天堂| 性色av一区二区三区在线观看| 在线播放国产一区中文字幕剧情欧美| 国产成人a亚洲精品| 欧洲成人在线视频| 亚洲国产97在线精品一区| 欧美午夜激情在线| 欧美中文在线观看| 色偷偷9999www| 亚洲精品v天堂中文字幕| 91网站在线看| 55夜色66夜色国产精品视频| 亚洲电影免费观看高清完整版在线| 色婷婷av一区二区三区久久| 黄色精品在线看| 亚洲精品网站在线播放gif| 亚洲综合精品一区二区| 国产精品无码专区在线观看| 亚洲美女免费精品视频在线观看| 中文字幕日韩视频| 欧美激情亚洲一区| 中文字幕日本精品| 亚洲黄色av女优在线观看| 欧美插天视频在线播放| 亚洲精品大尺度| 午夜精品一区二区三区在线视频| 欧美激情国产高清| 亚洲日韩欧美视频| 亚洲综合日韩在线| 国产精品久久久久久久久借妻| 中文字幕亚洲欧美在线| 国产精品女主播| 97视频在线观看免费高清完整版在线观看| 亚洲福利视频免费观看| 亚洲第一级黄色片| 亚洲乱码一区二区| 色综合色综合久久综合频道88| 国产精品91在线观看| 成人淫片在线看| 国产精品永久在线| 国产原创欧美精品| 久久久久久美女| 中文一区二区视频| 成人国产精品免费视频| 国产欧美va欧美va香蕉在线| 久久久久久久久久久久久久久久久久av| 久久久精品免费视频| 成人两性免费视频| 91高潮精品免费porn| 日韩成人在线观看| 成人av在线亚洲| 欧美国产视频一区二区| 日韩精品中文字| 91禁国产网站| 一区二区中文字幕| 国产精品成人一区二区三区吃奶| 国产欧美日韩丝袜精品一区| 黑人极品videos精品欧美裸| 国产视频久久久久久久| 国产精品久久久久久超碰| 久久视频在线免费观看| 最近的2019中文字幕免费一页| 国产精品免费久久久久久| 久久精品成人一区二区三区| 欧美黄色片在线观看| 日韩日本欧美亚洲| 青草热久免费精品视频| 大伊人狠狠躁夜夜躁av一区| 精品无人区太爽高潮在线播放| 久久国产精品久久久久久久久久| 91精品国产电影| 精品调教chinesegay| 国产网站欧美日韩免费精品在线观看| 欧美成人黄色小视频| 成人有码在线播放| 日韩小视频在线| 日韩av电影院| 亚洲色图激情小说| 国自产精品手机在线观看视频| 久久久国产成人精品| 国内精品一区二区三区四区| 97超碰蝌蚪网人人做人人爽| 国产精品成人观看视频国产奇米| 色综合伊人色综合网站| 久久中文字幕视频| 精品国产一区二区三区久久狼黑人| 精品久久久久国产| 久久人体大胆视频| 国产成人一区三区| 国产婷婷成人久久av免费高清| 国产午夜精品全部视频播放| 国产精品一香蕉国产线看观看| 国产精品视频一区二区高潮| 另类美女黄大片| 在线午夜精品自拍| 最近的2019中文字幕免费一页| 国产精品自产拍在线观| 亚洲成人久久网| 国产精品免费观看在线| 久久69精品久久久久久国产越南| 日本成人免费在线| 日韩电影大全免费观看2023年上| 久久6免费高清热精品| 亚洲一区二区三区乱码aⅴ| 富二代精品短视频| 欧美www在线| 亚洲欧洲自拍偷拍| 国产精品99久久久久久白浆小说| 国产精品成人观看视频国产奇米| 91精品在线看| 午夜精品一区二区三区在线播放| 成人福利网站在线观看| 午夜免费久久久久| 伊人久久精品视频| 国产亚洲激情在线| 亚洲欧美日本另类| 中文字幕亚洲综合久久筱田步美| 国产精品女人久久久久久| 久久网福利资源网站| 69av视频在线播放| 欧美不卡视频一区发布| 国产精品亚洲网站| 国产97在线|亚洲| 国产成人激情视频| 狠狠躁夜夜躁人人躁婷婷91| 久久天天躁狠狠躁夜夜躁2014|