>>deffoo(bar=[]):#bar是可選參數(shù),如果沒有提供bar的值,則默認(rèn)為[],...bar.append("baz")#但是稍后我們會(huì)看到這行代碼會(huì)出現(xiàn)問題。...returnbarPython程序員常" />

欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

十個(gè)Python程序員易犯的錯(cuò)誤

系統(tǒng) 1984 0

常見錯(cuò)誤1: 錯(cuò)誤地將表達(dá)式作為函數(shù)的默認(rèn)參數(shù)

在Python中,我們可以為函數(shù)的某個(gè)參數(shù)設(shè)置默認(rèn)值,使該參數(shù)成為可選參數(shù)。雖然這是一個(gè)很好的語言特性,但是當(dāng)默認(rèn)值是可變類型時(shí),也會(huì)導(dǎo)致一些令人困惑的情況。我們來看看下面這個(gè)Python函數(shù)定義:

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

          

Python程序員常犯的一個(gè)錯(cuò)誤,就是想當(dāng)然地認(rèn)為:在每次調(diào)用函數(shù)時(shí),如果沒有為可選參數(shù)傳入值,那么這個(gè)可選參數(shù)就會(huì)被設(shè)置為指定的默認(rèn)值。在上面的代碼中,你們可能覺得重復(fù)調(diào)用foo()函數(shù)應(yīng)該會(huì)一直返回'baz',因?yàn)槟銈兡J(rèn)每次foo()函數(shù)執(zhí)行時(shí)(沒有指定bar變量的值),bar變量都被設(shè)置為[](也就是,一個(gè)新的空列表)。

但是,實(shí)際運(yùn)行結(jié)果卻是這樣的:

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

很奇怪吧?為什么每次調(diào)用foo()函數(shù)時(shí),都會(huì)把"baz"這個(gè)默認(rèn)值添加到已有的列表中,而不是重新創(chuàng)建一個(gè)新的空列表呢?

答案就是,可選參數(shù)默認(rèn)值的設(shè)置在Python中只會(huì)被執(zhí)行一次,也就是定義該函數(shù)的時(shí)候。因此,只有當(dāng)foo()函數(shù)被定義時(shí),bar參數(shù)才會(huì)被初始化為默認(rèn)值(也就是,一個(gè)空列表),但是之后每次foo()函數(shù)被調(diào)用時(shí),都會(huì)繼續(xù)使用bar參數(shù)原先初始化生成的那個(gè)列表。

當(dāng)然,一個(gè)常見的解決辦法就是:

            
>>> 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: 錯(cuò)誤地使用類變量

我們來看下面這個(gè)例子:

            
>>> class A(object):
...   x = 1
...
>>> class B(A):
...   pass
...
>>> class C(A):
...   pass
...
>>> print A.x, B.x, C.x
1 1 1

          

這個(gè)結(jié)果很正常。

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

          

嗯,結(jié)果和預(yù)計(jì)的一樣。

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

          

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

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

請看下面這段代碼:

            
>>> try:
...   l = ["a", "b"]
...   int(l[2])
... except ValueError, IndexError: # To catch both exceptions, right?
...   pass
...
Traceback (most recent call last):
 File "
            
              "
            
            , line 3, in 
            
              
IndexError: list index out of range

            
          

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

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

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

          

常見錯(cuò)誤4: 錯(cuò)誤理解Python中的變量名解析

Python中的變量名解析遵循所謂的LEGB原則,也就是“L:本地作用域;E:上一層結(jié)構(gòu)中def或lambda的本地作用域;G:全局作用域;B:內(nèi)置作用域”(Local,Enclosing,Global,Builtin),按順序查找。看上去是不是很簡單?不過,事實(shí)上這個(gè)原則的生效方式還是有著一些特殊之處。說到這點(diǎn),我們就不得不提下面這個(gè)常見的Python編程錯(cuò)誤。請看下面的代碼:

            
>>> x = 10
>>> def foo():
...   x += 1
...   print x
...
>>> foo()
Traceback (most recent call last):
 File "
            
              "
            
            , line 1, in 
            
              
 File "
              
                "
              
              , line 2, in foo
UnboundLocalError: local variable 'x' referenced before assignment

            
          

出了什么問題?

上述錯(cuò)誤的出現(xiàn),是因?yàn)楫?dāng)你在某個(gè)作用域內(nèi)為變量賦值時(shí),該變量被Python解釋器自動(dòng)視作該作用域的本地變量,并會(huì)取代任何上一層作用域中相同名稱的變量。

正是因?yàn)檫@樣,才會(huì)出現(xiàn)一開始好好的代碼,在某個(gè)函數(shù)內(nèi)部添加了一個(gè)賦值語句之后卻出現(xiàn)了UnboundLocalError,難怪會(huì)讓許多人吃驚。

在使用列表時(shí),Python程序員尤其容易陷入這個(gè)圈套。

請看下面這個(gè)代碼示例:

            
>>> 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 "
            
              "
            
            , line 1, in 
            
              
 File "
              
                "
              
              , line 2, in foo
UnboundLocalError: local variable 'lst' referenced before assignment


            
          

呃?為什么函數(shù)foo1運(yùn)行正常,foo2卻出現(xiàn)了錯(cuò)誤?

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

常見錯(cuò)誤5: 在遍歷列表時(shí)更改列表

下面這段代碼的問題應(yīng)該算是十分明顯:

            
>>> 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 "
            
              "
            
            , line 2, in 
            
              
IndexError: list index out of range

            
          

在遍歷列表或數(shù)組的同時(shí)從中刪除元素,是任何經(jīng)驗(yàn)豐富的Python開發(fā)人員都會(huì)注意的問題。但是盡管上面的示例十分明顯,資深開發(fā)人員在編寫更為復(fù)雜代碼的時(shí)候,也很可能會(huì)無意之下犯同樣的錯(cuò)誤。

幸運(yùn)的是,Python語言融合了許多優(yōu)雅的編程范式,如果使用得當(dāng),可以極大地簡化代碼。簡化代碼還有一個(gè)好處,就是不容易出現(xiàn)在遍歷列表時(shí)刪除元素這個(gè)錯(cuò)誤。能夠做到這點(diǎn)的一個(gè)編程范式就是列表解析式。而且,列表解析式在避免這個(gè)問題方面尤其有用,下面用列表解析式重新實(shí)現(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)] # ahh, the beauty of it all
>>> numbers
[0, 2, 4, 6, 8]

          

常見錯(cuò)誤6: 不理解Python在閉包中如何綁定變量

請看下面這段代碼:

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

          

你可能覺得輸出結(jié)果應(yīng)該是這樣的:

但是,實(shí)際的輸出結(jié)果卻是:

嚇了一跳吧!

這個(gè)結(jié)果的出現(xiàn),主要是因?yàn)镻ython中的遲綁定(late binding )機(jī)制,即閉包中變量的值只有在內(nèi)部函數(shù)被調(diào)用時(shí)才會(huì)進(jìn)行查詢。因此,在上面的代碼中,每次create_multipliers()所返回的函數(shù)被調(diào)用時(shí),都會(huì)在附近的作用域中查詢變量i的值(而到那時(shí),循環(huán)已經(jīng)結(jié)束,所以變量i最后被賦予的值為4)。

要解決這個(gè)常見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

          

請注意!我們在這里利用了默認(rèn)參數(shù)來實(shí)現(xiàn)這個(gè)lambda匿名函數(shù)。有人可能認(rèn)為這樣做很優(yōu)雅,有人會(huì)覺得很巧妙,還有人會(huì)嗤之以鼻。但是,如果你是一名Python程序員,不管怎樣你都應(yīng)該要了解這種解決方法。

常見錯(cuò)誤7: 模塊之間出現(xiàn)循環(huán)依賴(circular dependencies)

假設(shè)你有兩個(gè)文件,分別是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()


          

首先,我們嘗試導(dǎo)入a.py模塊:

代碼運(yùn)行正常。也許這出乎了你的意料。畢竟,我們這里存在循環(huán)引用這個(gè)問題,想必應(yīng)該是會(huì)出現(xiàn)問題的,難道不是嗎?

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

那么回到我們的示例,當(dāng)我們導(dǎo)入a.py模塊時(shí),它在引用b.py模塊時(shí)是不會(huì)出現(xiàn)問題的,因?yàn)閎.py模塊在被引用時(shí),并不需要訪問在a.py模塊中定義的任何變量或函數(shù)。b.py模塊中對a模塊唯一的引用,就是調(diào)用了a模塊的foo()函數(shù)。但是那個(gè)函數(shù)調(diào)用發(fā)生在g()函數(shù)當(dāng)中,而a.py或b.py模塊中都沒有調(diào)用g()函數(shù)。所以,不會(huì)出現(xiàn)問題。

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

            
>>> import b
Traceback (most recent call last):
   File "
            
              "
            
            , line 1, in 
            
              
   File "b.py", line 1, in 
              
                
  import a
   File "a.py", line 6, in 
                
                  
 print f()
   File "a.py", line 4, in f
 return b.x
AttributeError: 'module' object has no attribute 'x'

                
              
            
          

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

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

            
x = 1

def g():
  import a # This will be evaluated only when g() is called
  print a.f()


          

現(xiàn)在我們再導(dǎo)入b.py模塊的話,就不會(huì)出現(xiàn)任何問題了:

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

          

常見錯(cuò)誤8: 模塊命名與Python標(biāo)準(zhǔn)庫模塊名沖突

Python語言的一大優(yōu)勢,就是其本身自帶的強(qiáng)大標(biāo)準(zhǔn)庫。但是,正因?yàn)槿绱耍绻悴蝗タ桃庾⒁獾脑挘阋彩怯锌赡転樽约旱哪K取一個(gè)和Python自帶標(biāo)準(zhǔn)庫模塊相同的名字(例如,如果你的代碼中有一個(gè)模塊叫email.py,那么這就會(huì)與Python標(biāo)準(zhǔn)庫中同名的模塊相沖突。)

這很可能會(huì)給你帶來難纏的問題。舉個(gè)例子,在導(dǎo)入模塊A的時(shí)候,假如該模塊A試圖引用Python標(biāo)準(zhǔn)庫中的模塊B,但卻因?yàn)槟阋呀?jīng)有了一個(gè)同名模塊B,模塊A會(huì)錯(cuò)誤地引用你自己代碼中的模塊B,而不是Python標(biāo)準(zhǔn)庫中的模塊B。這也是導(dǎo)致一些嚴(yán)重錯(cuò)誤的原因。

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

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

假設(shè)有下面這段代碼:

            
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,那么代碼運(yùn)行正常:

            
$ python foo.py 1
key error
1
$ python foo.py 2
value error
2

          

但是現(xiàn)在,我們換成Python 3再運(yùn)行一遍:

            
$ python3 foo.py 1
key error
Traceback (most recent call last):
 File "foo.py", line 19, in 
            
              
  bad()
 File "foo.py", line 17, in bad
  print(e)
UnboundLocalError: local variable 'e' referenced before assignment

            
          

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

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

            
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下運(yùn)行代碼:

            
$ python3 foo.py 1
key error
1
$ python3 foo.py 2
value error
2

          

太棒了!

常見錯(cuò)誤10: 錯(cuò)誤使用del方法

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

            
import foo

class Bar(object):
    ...
  def __del__(self):
    foo.cleanup(self.myhandle)
之后,你在another_mod.py文件中進(jìn)行如下操作:

import mod
mybar = mod.Bar()


          

如果你運(yùn)行another_mod.py模塊的話,將會(huì)出現(xiàn)AttributeError異常。

為什么?因?yàn)楫?dāng)解釋器結(jié)束運(yùn)行的時(shí)候,該模塊的全局變量都會(huì)被設(shè)置為None。因此,在上述示例中,當(dāng)__del__方法被調(diào)用之前,foo已經(jīng)被設(shè)置成了None。

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

應(yīng)用了上面這種方法,修改后的mod.py文件可能會(huì)是這樣子的:

            
import foo
import atexit

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


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


          

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

綜述

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

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


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 色视在线 | 在线观看免费视频日韩 | 亚洲日本一区二区三区 | 日韩深夜视频 | 嫩草影院观看 | 98色花堂国产第一页 | 久草视频免费播放 | 成人免费观看在线网址 | 欧美国产激情二区三区 | 狠狠色欧美亚洲狠狠色五 | www.久久久 | 午夜影院在线观看视频 | 91麻豆精品久久久久蜜臀 | 91青青国产在线观看免费 | 天天干天天干 | 成人免费一区二区三区视频网站 | 色3344| 国产你懂得 | 国产亚洲综合一区在线 | 中文成人在线 | 99精品视频在线观看 | 亚洲一区二区三区在线视频 | 蜜桃视频在线观看免费视频网站www | 久操青青| 天天看天天摸色天天综合网 | 青青久草| 欧美精品一区二区蜜臀亚洲 | 欧美老妇69交 | 欧美成人一级片 | 精品特级毛片 | 欧美成人黑人视频免费观看 | 精品国产一区二区三区免费 | 久九精品| a在线观看欧美在线观看 | 小明永久2015www永久免费观看 | 亚洲伊人色一综合网 | 亚洲欧美日韩精品久久 | 人人草视频在线 | 久爱视频www在线播放 | 深夜网址你懂的 | 欧美人在线 |