开心六月综合激情婷婷|欧美精品成人动漫二区|国产中文字幕综合色|亚洲人在线成视频

    1. 
      
        <b id="zqfy3"><legend id="zqfy3"><fieldset id="zqfy3"></fieldset></legend></b>
          <ul id="zqfy3"></ul>
          <blockquote id="zqfy3"><strong id="zqfy3"><dfn id="zqfy3"></dfn></strong></blockquote>
          <blockquote id="zqfy3"><legend id="zqfy3"></legend></blockquote>
          打開APP
          userphoto
          未登錄

          開通VIP,暢享免費電子書等14項超值服

          開通VIP
          寫 Python 時你要避免的十個錯誤
          后臺回復“大數據”,送你一份最全電子資料
          文 | MARTIN CHIKILIAN 譯 | EarlGrey
          推薦 | 編程派公眾號(ID:codingpy)
          不管是在學習還是工作過程中,人都會犯錯。雖然Python的語法簡單、靈活,但也一樣存在一些不小的坑,一不小心,初學者和資深Python程序員都有可能會栽跟頭。本文是Toptal網站的程序員梳理的10大常見錯誤,非常有參考意義。大家在開發(fā)過程中需要格外注意。
          常見錯誤1:錯誤地將表達式作為函數的默認參數
          在Python中,我們可以為函數的某個參數設置默認值,使該參數成為可選參數。雖然這是一個很好的語言特性,但是當默認值是可變類型時,也會導致一些令人困惑的情況。我們來看看下面這個Python函數定義:
          >>> def foo(bar=[]): # bar是可選參數,如果沒有提供bar的值,則默認為[],
          ... bar.append("baz") # 但是稍后我們會看到這行代碼會出現問題。
          ... return bar
          Python程序員常犯的一個錯誤,就是想當然地認為:在每次調用函數時,如果沒有為可選參數傳入值,那么這個可選參數就會被設置為指定的默認值。在上面的代碼中,你們可能覺得重復調用foo()函數應該會一直返回'baz',因為你們默認每次 foo()函數執(zhí)行時(沒有指定 bar變量的值), bar變量都被設置為[](也就是,一個新的空列表)。
          但是,實際運行結果卻是這樣的:
          >>> foo()
          ["baz"]
          >>> foo()
          ["baz", "baz"]
          >>> foo()
          ["baz", "baz", "baz"]
          很奇怪吧?為什么每次調用 foo()函數時,都會把"baz"這個默認值添加到已有的列表中,而不是重新創(chuàng)建一個新的空列表呢?
          答案就是,可選參數默認值的設置在Python中只會被執(zhí)行一次,也就是定義該函數的時候。因此,只有當 foo()函數被定義時, bar參數才會被初始化為默認值(也就是,一個空列表),但是之后每次 foo()函數被調用時,都會繼續(xù)使用 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
          在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>", 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>", line 1, in <module>
          File "<stdin>", line 2, in foo
          UnboundLocalError: 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>", line 1, in <module>
          File "<stdin>", line 2, in foo
          UnboundLocalError: 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>", line 2, in <module>
          IndexError: list index out of range
          在遍歷列表或數組的同時從中刪除元素,是任何經驗豐富的Python開發(fā)人員都會注意的問題。但是盡管上面的示例十分明顯,資深開發(fā)人員在編寫更為復雜代碼的時候,也很可能會無意之下犯同樣的錯誤。
          幸運的是,Python語言融合了許多優(yōu)雅的編程范式,如果使用得當,可以極大地簡化代碼。簡化代碼還有一個好處,就是不容易出現在遍歷列表時刪除元素這個錯誤。能夠做到這點的一個編程范式就是列表解析式。而且,列表解析式在避免這個問題方面尤其有用,下面用列表解析式重新實現上面代碼的功能:
          >>> 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)
          ...
          你可能覺得輸出結果應該是這樣的:
          0
          2
          4
          6
          8
          但是,實際的輸出結果卻是:
          8
          8
          8
          8
          8
          嚇了一跳吧!
          這個結果的出現,主要是因為Python中的遲綁定(late binding )機制,即閉包中變量的值只有在內部函數被調用時才會進行查詢。因此,在上面的代碼中,每次 create_multipliers()所返回的函數被調用時,都會在附近的作用域中查詢變量i的值(而到那時,循環(huán)已經結束,所以變量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)
          ...
          0
          2
          4
          6
          8
          請注意!我們在這里利用了默認參數來實現這個lambda匿名函數。有人可能認為這樣做很優(yōu)雅,有人會覺得很巧妙,還有人會嗤之以鼻。但是,如果你是一名Python程序員,不管怎樣你都應該要了解這種解決方法。
          常見錯誤7:模塊之間出現循環(huán)依賴(circular dependencies)
          假設你有兩個文件,分別是 a.py和 b.py,二者相互引用,如下所示:
          a.py文件中的代碼:
          import b
          def f():
          return b.x
          print f()
          b.py文件中的代碼:
          import a
          x = 1
          def g():
          print a.f()
          首先,我們嘗試導入 a.py模塊:
          >>> import a
          1
          代碼運行正常。也許這出乎了你的意料。畢竟,我們這里存在循環(huán)引用這個問題,想必應該是會出現問題的,難道不是嗎?
          答案是,僅僅存在循環(huán)引用的情況本身并不會導致問題。如果一個模塊已經被引用了,Python可以做到不再次進行引用。但是如果每個模塊試圖訪問其他模塊定義的函數或變量的時機不對,那么你就很可能陷入困境。
          那么回到我們的示例,當我們導入 a.py模塊時,它在引用 b.py模塊時是不會出現問題的,因為 b.py模塊在被引用時,并不需要訪問在 a.py模塊中定義的任何變量或函數。 b.py模塊中對a模塊唯一的引用,就是調用了a模塊的 foo()函數。但是那個函數調用發(fā)生在 g()函數當中,而 a.py或 b.py模塊中都沒有調用 g()函數。所以,不會出現問題。
          但是,如果我們試著導入 b.py模塊呢(即之前沒有引用 a.py模塊的前提下):
          >>> 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'
          糟糕。情況不太妙!這里的問題是,在導入 b.py的過程中,它試圖引用 a.py模塊,而 a.py模塊接著又要調用 foo()函數,這個 foo()函數接著又試圖去訪問 b.x變量。但是這個時候, b.x變量還沒有被定義,所以才出現了AttributeError異常。
          解決這個問題有一種非常簡單的方法,就是簡單地修改下 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語言的一大優(yōu)勢,就是其本身自帶的強大標準庫。但是,正因為如此,如果你不去刻意注意的話,你也是有可能為自己的模塊取一個和Python自帶標準庫模塊相同的名字(例如,如果你的代碼中有一個模塊叫 email.py,那么這就會與Python標準庫中同名的模塊相沖突。)
          這很可能會給你帶來難纏的問題。舉個例子,在導入模塊A的時候,假如該模塊A試圖引用Python標準庫中的模塊B,但卻因為你已經有了一個同名模塊B,模塊A會錯誤地引用你自己代碼中的模塊B,而不是Python標準庫中的模塊B。這也是導致一些嚴重錯誤的原因。
          因此,Python程序員要格外注意,避免使用與Python標準庫模塊相同的名稱。畢竟,修改自己模塊的名稱比提出PEP提議修改上游模塊名稱且讓提議通過,要來得容易的多。
          常見錯誤9:未能解決Python 2與Python 3之間的差異
          假設有下面這段代碼:
          import sys
          def 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 1
          key error
          1
          $ python foo.py 2
          value error
          2
          但是現在,我們換成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
          這到底是怎么回事?這里的“問題”是,在Python 3中,異常對象在 except代碼塊作用域之外是無法訪問的。(這么設計的原因在于,如果不這樣的話,堆棧幀中就會一直保留它的引用循環(huán),直到垃圾回收器運行,將引用從內存中清除。)
          避免這個問題的一種方法,就是在 except代碼塊的作用域之外,維持一個對異常對象的引用(reference),這樣異常對象就可以訪問了。下面這段代碼就使用了這種方法,因此在Python 2和Python 3中的輸出結果是一致的:
          import sys
          def 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 1
          key error
          1
          $ python3 foo.py 2
          value error
          2
          太棒了!
          常見錯誤10:錯誤使用del方法
          假設你在 mod.py的文件中編寫了下面的代碼:
          import foo
          class Bar(object):
          ...
          def __del__(self):
          foo.cleanup(self.myhandle)
          之后,你在 another_mod.py文件中進行如下操作:
          import mod
          mybar = mod.Bar()
          如果你運行 another_mod.py模塊的話,將會出現AttributeError異常。
          為什么?因為當解釋器結束運行的時候,該模塊的全局變量都會被設置為 None。因此,在上述示例中,當 __del__方法被調用之前, foo已經被設置成了 None。
          要想解決這個有點棘手的Python編程問題,其中一個辦法就是使用 atexit.register()方法。這樣的話,當你的程序執(zhí)行完成之后(即正常退出程序的情況下),你所指定的處理程序就會在解釋器關閉之前運行。
          應用了上面這種方法,修改后的 mod.py文件可能會是這樣子的:
          import foo
          import atexit
          def cleanup(handle):
          foo.cleanup(handle)
          class Bar(object):
          def __init__(self):
          ...
          atexit.register(cleanup, self.myhandle)
          這種實現支持在程序正常終止時干凈利落地調用任何必要的清理功能。很明顯,上述示例中將會由 foo.cleanup函數來決定如何處理 self.myhandle所綁定的對象。
          綜述
          Python是一門強大而又靈活的編程語言,提供的許多編程機制和范式可以極大地提高工作效率。但是與任何軟件工具或語言一樣,如果對該語言的能力理解有限或無法欣賞,那么有時候自己反而會被阻礙,而不是受益了。正如一句諺語所說,“自以為知道夠多,但實則會給自己或別人帶來危險”(knowing enough to be dangerous)。(譯者注:這句諺語的意思是,自以為已經對某件事情了解足夠,但在實際去執(zhí)行或實施時,卻會給自己和別人帶來危險。)
          不斷地熟悉Python語言的一些細微之處,尤其是本文中提到的10大常見錯誤,將會幫助你有效地使用這門語言,同時也能避免犯一些比較常見的錯誤。
          譯者:EarlGrey@編程派
          譯文鏈接:http://codingpy.com/article/top-10-mistakes-that-python-programmers-make
          原文鏈接:http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make
          印度小伙寫了套深度學習教程,Github上星標已經5000+
          GitHub熱榜第四!這套Python機器學習課,免費獲取還易吸收
          《流暢的 Python》到底好在哪?
          如何系統化學習 Python ?
          GitHub標星2.6萬!Python算法新手入門大全
          使用 Vue.js 和 Flask 實現全棧單頁面應用
          Python 實現一個自動化翻譯和替換的工具
          使用 Python 制作屬于自己的 PDF 電子書
          12步輕松搞定Python裝飾器
          200 行代碼實現 2048 游戲
          題圖:pexels,CC0 授權。
          本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現有害或侵權內容,請點擊舉報。
          打開APP,閱讀全文并永久保存 查看更多類似文章
          猜你喜歡
          類似文章
          Python程序員最常犯的10個錯誤,你中招了嗎?
          Python 程序員經常犯的 10 個錯誤
          Python 2 和 Python 3 主要區(qū)別有哪些(一)
          《源碼探秘 CPython》48. 名字、作用域、名字空間(下)
          從零開始學Python:第八課-函數和模塊
          Python最差實踐
          更多類似文章 >>
          生活服務
          分享 收藏 導長圖 關注 下載文章
          綁定賬號成功
          后續(xù)可登錄賬號暢享VIP特權!
          如果VIP功能使用有故障,
          可點擊這里聯系客服!

          聯系客服