題圖:Photo by Andrew Ridley on Unsplash
設(shè)計模式是前人多年總結(jié)出來的經(jīng)驗,而反設(shè)計模式(Anti-Pattern)就是那些違反正確方式寫代碼的方法,往往這樣的代碼從可讀性、安全性、正確性等方面都有問題。今天列一些平時寫代碼的壞習(xí)慣,避開這些問題使得我們的代碼效率更高,可讀性更強,Bug更少。
py文件名跟系統(tǒng)自帶的模塊名一樣導(dǎo)致找不到模塊是初學(xué)者犯錯最多的時候,我最開始也犯這樣的錯,比如學(xué) random 模塊時,將自己的文件名也命名為 random.py, 執(zhí)行的時候報錯。為什么?因為 你在import random的時候,解釋器有優(yōu)先從當(dāng)前目錄加載模塊,剛好,當(dāng)前目錄有個random.py ,所以就不會去Python的標(biāo)準庫目錄找random模塊了。
# random.py
import random
print(random.choice([1, 2, 3]))
AttributeError: module 'random' has no attribute 'choice'
程序員最頭疼的事情就是如何給變量命名,如何給類命名,如何給函數(shù)命名,有種不好的習(xí)慣就是我們?yōu)榱送祽谢蛘呦氩怀龊玫拿謺r,直接使用內(nèi)建函數(shù)或者內(nèi)建模塊的名字來命名,例如:
id = 5
len = 3
list = [1,2,3]
str = 'jack'
以上幾個變量名都是系統(tǒng)內(nèi)建函數(shù)名稱,一旦被你自己的變量名占用后,后面要使用該函數(shù)時直接報錯
>>> str(1)
TypeError: 'str' object is not callable
最佳的命名方式要做到見名知義,避免與內(nèi)建函數(shù)沖突,如果實在想不到更好的名字,可以考慮加下劃線
id_ = 5length = 3numbers = [1,2,3]name = 'jack'
你第一次碰到下面這段代碼的時候,可能你會很驚訝,這怎么會報錯?
a = 1
def fun():
a += 2
print(a)
fun()
報 UnboundLocalError 錯誤
UnboundLocalError: local variable 'a' referenced before assignment
規(guī)則:
如果變量在函數(shù)中被引用沒有被賦值,那么就是全局變量
如果變量在函數(shù)的任意位置被賦予過新的值,那么該變量就是局部變量
如果變量在函數(shù)中重新賦予了值,又希望是全部變量,則需要用關(guān)鍵字 global
修飾該變量
a = 1
def fun():
global a
a += 2
print(a)
fun()
這個例子可能你在其他地方有看過,如果面試官問題,為什么會這樣的時候,你是否能答出來。
def func(i=0, nums=[]):
nums.append(i)
print(i)
print(nums)
func(i=1)
func(i=1)
輸出結(jié)果
1
[1]
1
[1, 1]
一個函數(shù)調(diào)用兩次,結(jié)果卻不一樣,這是什么原因?初學(xué)者以為這是 Python 的 bug,其實這是陷阱,函數(shù)自身也是對象,默認參數(shù)會作為該函數(shù)對象的兩個屬性存在。類似于:func.i, func.nums。函數(shù)創(chuàng)建之后,nums 已經(jīng)有一個默認值 []
,第一次調(diào)用時,相當(dāng)于 func.nums.append(1), 第二次調(diào)用相當(dāng)于 func.nums.append(2),因為列表是可變對象,所以,每調(diào)用一次,就往列表里面增加了一個元素。
正確的實現(xiàn)方法是:
def func(i=0, nums=None):
if nums is None:
nums = []
nums.append(i)
print(i)
print(nums)
func(i=1)
func(i=1)
將一個列表中的所有元素做平方處理,普通的做法就是新建一個列表,逐個迭代計算出每個值的平方,再加入到新數(shù)組中。喜歡裝X的可能會把代碼寫的巨難懂,各種技巧都給你用上,最后只為實現(xiàn)一個簡單的需求。90% 的情況下,列表推導(dǎo)式可以代替 map、filter 函數(shù)。
普通寫法
items = [1, 2, 3, 4, 5]squared = []for i in items: squared.append(i**2)
裝B寫法
items = [1, 2, 3, 4, 5]squared = list(map(lambda x: x**2, items))
最佳寫法
items = [1, 2, 3, 4, 5]
squared = [x**2 for x in items]
在某些場景,不可避免需要用到列表元素中的下標(biāo)索引位置,一般程序員的做法就是按照最原始C語言的寫法
普通寫法
color = ['red', 'blue', 'green']
for i in range(len(color)):
print(i, color[i])
裝B寫法
color = ['red', 'blue', 'green']
for i, item in zip(range(len(color)), color):
print(i, item)
最佳寫法
color = ['red', 'blue', 'green']
for i, item in enumerate(color):
print(i, item)
偷懶的程序員喜歡直接 import * ,雖然能節(jié)省一些代碼,但是 import *里面隱藏著一些潛在的危險。如果 a,b 兩個包里面都有一個叫做 foo 的模塊,那么其中一個就會被覆蓋。正確的做法就是顯示地把需要用到的模塊 import 進來,如果遇到重名的模塊,則用 as 將其重命名。
from a import foo as foo_a
from b import foo as foo_b
Python2.7 在2020年官方不再維護,現(xiàn)在都2019年了,所以如果你的系統(tǒng)特別是新系統(tǒng)還用Python2.7的話,不失為最差的開發(fā)實踐,所以,趕緊升級到Python3.6吧