Python開發(fā)者必定會進(jìn)的十大神坑,你入過坑沒踏拜?

摘要:Python是一門簡單易學(xué)的編程語言碎赢,語法簡潔而清晰,并且擁有豐富和強(qiáng)大的類庫速梗。在日常開發(fā)中肮塞,開發(fā)者很容犯一些低級的錯誤,本文總結(jié)了開發(fā)者最容易犯的10個錯誤姻锁。

小編推薦大家可以加我的扣扣群 735934841 峦嗤。里面有海量視頻教程和學(xué)習(xí)資料免費(fèi)領(lǐng)取,不失為是一個學(xué)習(xí)的好地方屋摔,歡迎你的到來烁设。一起交流學(xué)習(xí)!共同進(jìn)步5鍪浴装黑!

Python是一門簡單易學(xué)的編程語言,語法簡潔而清晰弓熏,并且擁有豐富和強(qiáng)大的類庫恋谭。與其它大多數(shù)程序設(shè)計(jì)語言使用大括號不一樣 ,它使用縮進(jìn)來定義語句塊挽鞠。

在平時(shí)的工作中疚颊,Python開發(fā)者很容易犯一些小錯誤,這些錯誤都很容易避免信认,本文總結(jié)了Python開發(fā)者最常犯的10個錯誤材义,一起來看下,不知你中槍了沒有嫁赏。

1.濫用表達(dá)式作為函數(shù)參數(shù)默認(rèn)值

Python允許開發(fā)者指定一個默認(rèn)值給函數(shù)參數(shù)其掂,雖然這是該語言的一個特征,但當(dāng)參數(shù)可變時(shí)潦蝇,很容易導(dǎo)致混亂款熬,例如,下面這段函數(shù)定義:

>>> def foo(bar=[]): # bar is optional and defaults to [] if not specified

... bar.append("baz") # but this line could be problematic, as we'll see...

... return bar

在上面這段代碼里攘乒,一旦重復(fù)調(diào)用foo()函數(shù)(沒有指定一個bar參數(shù))贤牛,那么將一直返回'bar',因?yàn)闆]有指定參數(shù)则酝,那么foo()每次被調(diào)用的時(shí)候殉簸,都會賦予[]。下面來看看,這樣做的結(jié)果:

>>> foo()

["baz"]

>>> foo()

["baz", "baz"]

>>> foo()

["baz", "baz", "baz"]

解決方案:

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

僅僅是改變了A.x喂链,為什么C.x也跟著改變了返十。

在Python中,類變量都是作為字典進(jìn)行內(nèi)部處理的椭微,并且遵循方法解析順序(MRO)洞坑。在上面這段代碼中,因?yàn)閷傩詘沒有在類C中發(fā)現(xiàn)蝇率,它會查找它的基類(在上面例子中只有A迟杂,盡管Python支持多繼承)。換句話說本慕,就是C自己沒有x屬性排拷,獨(dú)立于A,因此锅尘,引用 C.x其實(shí)就是引用A.x监氢。

3.為異常指定不正確的參數(shù)

假設(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中,except Exception,e通常是用來綁定異常里的 第二參數(shù)顿乒,好讓其進(jìn)行更進(jìn)一步的檢查议街。因此,在上面這段代碼里璧榄,IndexError異常并沒有被except語句捕獲特漩,異常最后被綁定 到了一個名叫IndexError的參數(shù)上。

在一個異常語句里捕獲多個異常的正確方法是指定第一個參數(shù)作為一個元組骨杂,該元組包含所有被捕獲的異常涂身。與此同時(shí),使用as關(guān)鍵字來保證最大的可移植性腊脱,Python 2和Python 3都支持該語法访得。

>>> try:

... l = ["a", "b"]

... int(l[2])

... except (ValueError, IndexError) as e:

... pass

...

>>>

4.誤解Python規(guī)則范圍

Python的作用域解析是基于LEGB規(guī)則,分別是Local陕凹、Enclosing、Global鳄炉、Built-in杜耙。實(shí)際上,這種解析方法也有一些玄機(jī)拂盯,看下面這個例子:

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

許多人會感動驚訝佑女,當(dāng)他們在工作的函數(shù)體里添加一個參數(shù)語句,會在先前工作的代碼里報(bào)UnboundLocalError錯誤( 點(diǎn)擊這里查看更詳細(xì)描述)。

在使用列表時(shí)团驱,開發(fā)者是很容易犯這種錯誤的摸吠,看看下面這個例子:

>>> lst = [1, 2, 3]

>>> def foo1():

lst.append(5) # This works ok

>>> foo1()

>>> lst

[1, 2, 3, 5]

>>> lst = [1, 2, 3]

>>> def foo2():

... lst += [5] # ... but this bombs!

...

>>> foo2()

Traceback (most recent call last):

File "", line 1, in

File "", line 2, in foo

UnboundLocalError: local variable 'lst' referenced before assignment

為什么foo2失敗而foo1運(yùn)行正常?

答案與前面那個例子是一樣的嚎花,但又有一些微妙之處寸痢。foo1沒有賦值給lst,而foo2賦值了紊选。lst += [5]實(shí)際上就是lst = lst + [5]啼止,試圖給lst賦值(因此,假設(shè)Python是在局部作用域里)兵罢。然而献烦,我們正在尋找指定給lst的值是基于lst本身,其實(shí)尚未確定卖词。

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 "", line 2, in

IndexError: list index out of range

在遍歷的時(shí)候巩那,對列表進(jìn)行刪除操作,這是很低級的錯誤此蜈。稍微有點(diǎn)經(jīng)驗(yàn)的人都不會犯即横。

對上面的代碼進(jìn)行修改,正確地執(zhí)行:

>>> 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.如何在閉包中綁定變量

看下面這個例子:

>>> def create_multipliers():

... return [lambda x : i * x for i in range(5)]

>>> for multiplier in create_multipliers():

... print multiplier(2)

...

你期望的結(jié)果是:

0

2

4

6

8

實(shí)際上:

8

8

8

8

8

是不是非常吃驚舶替!出現(xiàn)這種情況主要是因?yàn)镻ython的后期綁定行為令境,該變量在閉包中使用的同時(shí),內(nèi)部函數(shù)又在調(diào)用它顾瞪。

解決方案:

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

7.創(chuàng)建循環(huán)模塊依賴關(guā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

可以很好地工作,也許你會感到驚訝钉跷。畢竟弥鹦,我們確實(shí)在這里做了一個循環(huán)導(dǎo)入,難道不應(yīng)該有點(diǎn)問題嗎爷辙?

僅僅存在一個循環(huán)導(dǎo)入并不是Python本身問題彬坏,如果一個模塊被導(dǎo)入,Python就不會試圖重新導(dǎo)入膝晾。根據(jù)這一點(diǎn)栓始,每個模塊在試圖訪問函數(shù)或變量時(shí),可能會在運(yùn)行時(shí)遇到些問題血当。

當(dāng)我們試圖導(dǎo)入b.py會發(fā)生什么(先前沒有導(dǎo)入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的過程中還要試圖導(dǎo)入a.py,這樣就要調(diào)用f()落恼,并且試圖訪問b.x箩退。但是b.x并未被定義。

可以這樣解決佳谦,僅僅修改b.py導(dǎo)入到a.py中的g()函數(shù):

x = 1

def g():

import a# This will be evaluated only when g() is called

print a.f()

無論何時(shí)導(dǎo)入戴涝,一切都可以正常運(yù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'

8.與Python標(biāo)準(zhǔn)庫模塊名稱沖突

Python擁有非常豐富的模塊庫,并且支持“開箱即用”吠昭。因此喊括,如果不刻意避免,很容易發(fā)生命名沖突事件矢棚。例如郑什,在你的代碼中可能有一個email.py的模塊,由于名稱一致蒲肋,它很有可能與Python自帶的標(biāo)準(zhǔn)庫模塊發(fā)生沖突蘑拯。

9.未按規(guī)定處理Python2.x和Python3.x之間的區(qū)別

看一下foo.py:

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

但是在Python 3里:

$ 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

解決方案:

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

在Py3k中運(yùn)行結(jié)果:

$ python3 foo.py 1

key error

1

$ python3 foo.py 2

value error

2

在 Python招聘指南里有許多關(guān)于Python 2與Python 3在移植代碼時(shí)需要關(guān)注的注意事項(xiàng)與討論,大家可以前往看看兜粘。

10.濫用__del__方法

比如這里有一個叫mod.py的文件:

import foo

class Bar(object):

...

def __del__(self):

foo.cleanup(self.myhandle)

下面申窘,你在another_mod.py文件里執(zhí)行如下操作:

import mod

mybar = mod.Bar()

你會獲得一個AttributeError異常。

至于為什么會出現(xiàn)該異常孔轴,點(diǎn)擊這里查看詳情剃法。當(dāng)解釋器關(guān)閉時(shí),該模塊的全局變量全部設(shè)置為None路鹰。因此贷洲,在上面這個例子里,當(dāng)__del__被調(diào)用時(shí)晋柱,foo已經(jīng)全部被設(shè)置為None优构。

一個很好的解決辦法是使用atexit.register()代替。順便說一句雁竞,當(dāng)程序執(zhí)行完成后钦椭,您注冊的處理程序會在解釋器關(guān)閉之前停止 工作。

修復(fù)上面問題的代碼:

import foo

import atexit

def cleanup(handle):

foo.cleanup(handle)

class Bar(object):

def __init__(self):

...

atexit.register(cleanup, self.myhandle)

在程序的正常終止的前提下碑诉,這個實(shí)現(xiàn)提供了一個整潔可靠的方式調(diào)用任何需要清理的功能彪腔。

總結(jié)

Python是一款強(qiáng)大而靈活的編程語言,并且?guī)в性S多機(jī)制和模式來大大提高工作效率进栽。正如任何一門語言或軟件工具一樣漫仆,人們對其能力都會存在一個限制性地理解或欣賞,有些是弊大于利泪幌,有些時(shí)候反而會帶來一些陷進(jìn)盲厌。 體會一名語言的細(xì)微之處,理解一些常見的陷阱祸泪,有助于你在開發(fā)者的道路上走的更遠(yuǎn)吗浩。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市没隘,隨后出現(xiàn)的幾起案子懂扼,更是在濱河造成了極大的恐慌,老刑警劉巖右蒲,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阀湿,死亡現(xiàn)場離奇詭異,居然都是意外死亡瑰妄,警方通過查閱死者的電腦和手機(jī)陷嘴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來间坐,“玉大人灾挨,你說我怎么就攤上這事≈袼危” “怎么了劳澄?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蜈七。 經(jīng)常有香客問我秒拔,道長,這世上最難降的妖魔是什么飒硅? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任砂缩,我火速辦了婚禮,結(jié)果婚禮上狡相,老公的妹妹穿的比我還像新娘梯轻。我一直安慰自己,他們只是感情好尽棕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布喳挑。 她就那樣靜靜地躺著,像睡著了一般滔悉。 火紅的嫁衣襯著肌膚如雪伊诵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天回官,我揣著相機(jī)與錄音曹宴,去河邊找鬼。 笑死歉提,一個胖子當(dāng)著我的面吹牛笛坦,可吹牛的內(nèi)容都是我干的区转。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼版扩,長吁一口氣:“原來是場噩夢啊……” “哼废离!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起礁芦,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜻韭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后柿扣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肖方,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年未状,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了俯画。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡娩践,死狀恐怖活翩,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情翻伺,我是刑警寧澤材泄,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吨岭,受9級特大地震影響拉宗,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辣辫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一旦事、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧急灭,春花似錦姐浮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至畴嘶,卻和暖如春蛋逾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背窗悯。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工区匣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蒋院。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓亏钩,卻偏偏與公主長得像莲绰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子铸屉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容