日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

【Python】远离 Python 最差实践,避免挖坑

發(fā)布時(shí)間:2024/8/26 python 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Python】远离 Python 最差实践,避免挖坑 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文鏈接:http://blog.guoyb.com/2016/12/03/bad-py-style/

最近在看一些陳年老系統(tǒng),其中有一些不好的代碼習(xí)慣遺留下來(lái)的坑;加上最近自己也寫(xiě)了一段爛代碼導(dǎo)致服務(wù)器負(fù)載飆升,所以就趁此機(jī)會(huì)總結(jié)下我看到過(guò)/寫(xiě)過(guò)的自認(rèn)為不好的 Python 代碼習(xí)慣,時(shí)刻提醒自己遠(yuǎn)離這些“最差實(shí)踐”,避免挖坑。

下面所舉的例子中,有一部分會(huì)造成性能問(wèn)題,有一部分會(huì)導(dǎo)致隱藏 bug,或日后維護(hù)、重構(gòu)困難,還有一部分純粹是我認(rèn)為不夠 pythonic。所以大家自行甄別,取精去糟吧。

函數(shù)默認(rèn)參數(shù)使用可變對(duì)象

這個(gè)例子我想大家應(yīng)該在各種技術(shù)文章中見(jiàn)過(guò)許多遍了,也足以證明這是一個(gè)大坑。

先看錯(cuò)誤示范吧:

def use_mutable_default_param(idx=0, ids=[]):ids.append(idx)print(idx)print(ids)use_mutable_default_param(idx=1) use_mutable_default_param(idx=2)輸出: 1 [1] 2 [1, 2]

理解這其中的原因,最重要的是有兩點(diǎn):
函數(shù)本身也是一個(gè)對(duì)象,默認(rèn)參數(shù)綁定于這個(gè)函數(shù)對(duì)象上
append 這類(lèi)方法會(huì)直接修改對(duì)象,所以下次調(diào)用此函數(shù)時(shí),其綁定的默認(rèn)參數(shù)已經(jīng)不再是空l(shuí)ist了

正確的做法如下:

def donot_use_mutable_default_param(idx=0, ids=None):if ids is None:ids = []ids.append(idx)print(idx)print(ids)

try…except不具體指明異常類(lèi)型

雖然在 Python 中使用 try…except 不會(huì)帶來(lái)嚴(yán)重的性能問(wèn)題,但是不加區(qū)分,直接捕獲所有類(lèi)型異常的做法,往往會(huì)掩蓋掉其他的 bug,造成難以追查的 bug。

一般的,我覺(jué)得應(yīng)該盡量少的使用 try…except,這樣可以在開(kāi)發(fā)期盡早的發(fā)現(xiàn)問(wèn)題。即使要使用 try…except,也應(yīng)該盡可能的指定出要捕獲的具體異常,并在 except 語(yǔ)句中將異常信息記入 log,或者處理完之后,再直接raise出來(lái)。

關(guān)于dict的冗余代碼

我經(jīng)常能夠看到這樣的代碼:

d = {} datas = [1, 2, 3, 4, 2, 3, 4, 1, 5] for k in datas:if k not in d:d[k] = 0 d[k] += 1

其實(shí),完全可以使用 collections.defaultdict 這一數(shù)據(jù)結(jié)構(gòu)更簡(jiǎn)單優(yōu)雅的實(shí)現(xiàn)這樣的功能:

default_d = defaultdict(lambda: 0) datas = [1, 2, 3, 4, 2, 3, 4, 1, 5] for k in datas:default_d[k] += 1

同樣的,這樣的代碼:

# d is a dict if 'list' not in d: d['list'] = [] d['list'].append(x)

完全可以用這樣一行代碼替代:

# d is a dict d.setdefault('list', []).append(x)

同樣的,下面這兩種寫(xiě)法一看就是帶有濃濃的C味兒:

# d is a dict for k in d: v = d[k] # do something# l is a list for i in len(l): v = l[i] # do something

應(yīng)該用更 pythonic 的寫(xiě)法:

# d is a dict for k, v in d.iteritems(): # do something pass# l is a list for i, v in enumerate(l): # do something pass

另外,enumerate 其實(shí)還有個(gè)第二參數(shù),表示序號(hào)從幾開(kāi)始。如果想要序號(hào)從1開(kāi)始數(shù)起,可以使用 enumerate(l, 1)。

使用flag變量而不使用for…else語(yǔ)句

同樣,這樣的代碼也很常見(jiàn):

search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey'] found = False for s in search_list:if s.startswith('C'):found = True# do something when foundprint('Found')breakif not found:# do something when not foundprint('Not found')

其實(shí),用 for…else 更優(yōu)雅:

search_list = ['Jone', 'Aric', 'Luise', 'Frank', 'Wey'] for s in search_list:if s.startswith('C'):# do something when foundprint('Found')break else:# do something when not foundprint('Not found')

過(guò)度使用 tuple unpacking

在 Python 中,允許對(duì) tuple 類(lèi)型進(jìn)行 unpack 操作,如下所示:

# human = ('James', 180, 32) name,height,age = human

這個(gè)特性用起來(lái)很爽,比寫(xiě) name=human[0] 之類(lèi)的不知道高到哪里去了。所以,這一特性往往被濫用,一個(gè) human 在程序的各處通過(guò)上面的方式 unpack。

然而如果后來(lái)需要在 human 中插入一個(gè)表示性別的數(shù)據(jù) sex,那么對(duì)于所有的這種 unpack 都需要進(jìn)行修改,即使在有些邏輯中并不會(huì)使用到性別。

# human = ('James', 180, 32) name,height,age, _ = human # or # name, height, age, sex = human

有如下幾種方式解決這一問(wèn)題:
老老實(shí)實(shí)寫(xiě) name=human[0] 這種代碼,在需要使用性別信息處加上 sex=human[3]
使用 dict 來(lái)表示 human
使用 namedtuple

# human = namedtuple('human', ['name', 'height', 'age', 'sex']) h = human('James', 180, 32, 0) # then you can use h.name, h.sex and so on everywhere.

到處都是 import *

import * 是一種懶惰的行為,它不僅會(huì)污染當(dāng)前的命名空間,并且還會(huì)使得 pyflakes 等代碼檢查工具失效。在后續(xù)查看代碼或者 debug 的過(guò)程中,往往也很難從一堆 import * 中找到一個(gè)第三方函數(shù)的來(lái)源。

可以說(shuō)這種習(xí)慣是百害而無(wú)一利的。

文件操作

文件操作不要使用裸奔的f = open(‘filename’)了,使用with open(‘filename’) as f來(lái)讓context manager幫你處理異常情況下的關(guān)閉文件等亂七八糟的事情多好。

野蠻使用 class.name 判斷類(lèi)型

我曾經(jīng)遇見(jiàn)過(guò)一個(gè) bug:為了實(shí)現(xiàn)某特定功能,我新寫(xiě)了一個(gè) class B(A),在 B 中重寫(xiě)了 A 的若干函數(shù)。整個(gè)實(shí)現(xiàn)很簡(jiǎn)單,但是就是有一部分 A 的功能無(wú)法生效。最后追查到的原因,就是在一些邏輯代碼中,硬性的判斷了entity.__class__.__name__ == ‘A’。

除非你就是想限定死繼承層級(jí)中的當(dāng)前類(lèi)型(也就是,屏蔽未來(lái)可能會(huì)出現(xiàn)的子類(lèi)),否則,不要使用 class.__name__,而改用 isinstance 這個(gè)內(nèi)建函數(shù)。畢竟,Python 把這兩個(gè)變量的名字都刻意帶上那么多下劃線(xiàn),本來(lái)就是不太想讓你用嘛。

循環(huán)內(nèi)部有多層函數(shù)調(diào)用

循環(huán)內(nèi)部有多層函數(shù)調(diào)用,有如下兩方面的隱患:
Python 沒(méi)有 inline 函數(shù),所以函數(shù)調(diào)用本來(lái)就會(huì)導(dǎo)致一定的開(kāi)銷(xiāo),尤其是本身邏輯簡(jiǎn)單的時(shí)候,這個(gè)開(kāi)銷(xiāo)所占的比例就會(huì)挺可觀(guān)的。
更嚴(yán)重的是,在之后維護(hù)這份代碼時(shí),會(huì)容易讓人忽略掉函數(shù)是在循環(huán)中被調(diào)用的,所以容易在函數(shù)內(nèi)部添加了一些開(kāi)銷(xiāo)較大卻不必每次循環(huán)都調(diào)用的函數(shù),比如 time.localtime()。如果是直接一個(gè)平鋪直敘的循環(huán),我想大部分的程序員都應(yīng)該知道把 time.localtime()寫(xiě)到循環(huán)的外面,但是引入多層的函數(shù)調(diào)用之后,就不一定了哦。

所以我建議,在循環(huán)內(nèi)部,如非特別復(fù)雜的邏輯,都應(yīng)該直接寫(xiě)在循環(huán)里,不要進(jìn)行函數(shù)調(diào)用。如果一定要包裝一層函數(shù)調(diào)用,應(yīng)該在函數(shù)的命名或注釋中,提示后續(xù)的維護(hù)者,這個(gè)函數(shù)會(huì)在循環(huán)內(nèi)部使用。


Python 是一門(mén)非常容易入門(mén)的語(yǔ)言,嚴(yán)格的縮進(jìn)要求和豐富的內(nèi)置數(shù)據(jù)類(lèi)型,使得大部分 Python 代碼都能做到比較好的規(guī)范。但是,不嚴(yán)格要求自己,也很容易就寫(xiě)出犯二的代碼。上面列出的只是很小的一部分,唯有多讀、多寫(xiě)、多想,才能培養(yǎng)敏銳的代碼嗅覺(jué),第一時(shí)間發(fā)現(xiàn)壞味道啊。歡迎大家補(bǔ)充~

轉(zhuǎn)載于:https://www.cnblogs.com/Detector/p/8652968.html

總結(jié)

以上是生活随笔為你收集整理的【Python】远离 Python 最差实践,避免挖坑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。