亚洲熟女综合色一区二区三区,亚洲精品中文字幕无码蜜桃,亚洲va欧美va日韩va成人网,亚洲av无码国产一区二区三区,亚洲精品无码久久久久久久

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

不管是在學(xué)習(xí)還是工作過程中,人都會犯錯。雖然Python的語法簡單、靈活,但也一樣存在一些不小的坑,一不小心,初學(xué)者和資深Python程序員都有可能會栽跟頭。本文是Toptal網(wǎng)站的程序員梳理的10大常見錯誤,非常有參考意義。大家在開發(fā)過程中需要格外注意。

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


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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

但是,實際運行結(jié)果卻是這樣的:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

常見問題2:錯誤地使用類變量


我們來看下面這個例子:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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


請看下面這段代碼:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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


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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

出了什么問題?

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

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

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

請看下面這個代碼示例:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

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


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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

幸運的是,Python語言融合了許多優(yōu)雅的編程范式,如果使用得當(dāng),可以極大地簡化代碼。簡化代碼還有一個好處,就是不容易出現(xiàn)在遍歷列表時刪除元素這個錯誤。能夠做到這點的一個編程范式就是列表解析式。而且,列表解析式在避免這個問題方面尤其有用,下面用列表解析式重新實現(xiàn)上面代碼的功能:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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


請看下面這段代碼:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

嚇了一跳吧!

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

要解決這個常見Python問題的方法中,需要使用一些hack技巧:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

假設(shè)你有兩個文件,分別是a.pyb.py,二者相互引用,如下所示:

a.py文件中的代碼:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

b.py文件中的代碼:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

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

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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


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

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

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

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


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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

在Python 3下運行代碼:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

太棒了!

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


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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

之后,你在another_mod.py文件中進行如下操作:

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

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

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

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

Python剛?cè)腴T愛犯的幾大錯誤:快笑死人了

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

綜述


Python是一門強大而又靈活的編程語言,提供的許多編程機制和范式可以極大地提高工作效率。但是與任何軟件工具或語言一樣,如果對該語言的能力理解有限或無法欣賞,那么有時候自己反而會被阻礙,而不是受益了。正如一句諺語所說,“自以為知道夠多,但實則會給自己或別人帶來危險”(knowing enough to be dangerous)。(譯者注:這句諺語的意思是,自以為已經(jīng)對某件事情了解足夠,但在實際去執(zhí)行或?qū)嵤r,卻會給自己和別人帶來危險。)

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


相關(guān)新聞

歷經(jīng)多年發(fā)展,已成為國內(nèi)好評如潮的Linux云計算運維、SRE、Devops、網(wǎng)絡(luò)安全、云原生、Go、Python開發(fā)專業(yè)人才培訓(xùn)機構(gòu)!