來自:Python之禪(微訊號:VTtalk)
設計樣式是前人多年總結出來的經驗,而反設計樣式(Anti-Pattern)就是那些違反正確方式寫程式碼的方法,往往這樣的程式碼從可讀性、安全性、正確性等方面都有問題。今天列一些平時寫程式碼的壞習慣,避開這些問題使得我們的程式碼效率更高,可讀性更強,Bug更少。
1、使用標準庫中的名字
py檔案名跟系統自帶的模組名一樣導致找不到模組是初學者犯錯最多的時候,我最開始也犯這樣的錯,比如學 random 模組時,將自己的檔案名也命名為 random.py, 執行的時候報錯。為什麼?因為 你在import random的時候,直譯器有優先從當前目錄載入模組,剛好,當前目錄有個random.py ,所以就不會去Python的標準庫目錄找random模組了。
# random.py
import random
print(random.choice([1, 2, 3]))
AttributeError: module 'random' has no attribute 'choice'
2. 使用內建名稱作為變數名
程式員最頭疼的事情就是如何給變數命名,如何給類命名,如何給函式命名,有種不好的習慣就是我們為了偷懶或者想不出好的名字時,直接使用內建函式或者內建模組的名字來命名,例如:
id = 5
len = 3
list = [1,2,3]
str = "jack"
以上幾個變數名都是系統內建函式名稱,一旦被你自己的變數名佔用後,後面要使用該函式時直接報錯
>>> str(1)
TypeError: 'str' object is not callable
最佳的命名方式要做到見名知義,避免與內建函式衝突,如果實在想不到更好的名字,可以考慮加下劃線
id_ = 5length = 3numbers = [1,2,3]name = "jack"
3、變數作用域
你第一次碰到下麵這段程式碼的時候,可能你會很驚訝,這怎麼會報錯?
a = 1
def fun():
a += 2
print(a)
fun()
報 UnboundLocalError 錯誤
UnboundLocalError: local variable 'a' referenced before assignment
規則:
- 如果變數在函式中被取用沒有被賦值,那麼就是全域性變數
- 如果變數在函式的任意位置被賦予過新的值,那麼該變數就是區域性變數
- 如果變數在函式中重新賦予了值,又希望是全部變數,則需要用關鍵字
global
修飾該變數
a = 1
def fun():
global a
a += 2
print(a)
fun()
4、使用可變物件作為預設引數
這個例子可能你在其他地方有看過,如果面試官問題,為什麼會這樣的時候,你是否能答出來。
def func(i=0, nums=[]):
nums.append(i)
print(i)
print(nums)
func(i=1)
func(i=1)
輸出結果
1
[1]
1
[1, 1]
一個函式呼叫兩次,結果卻不一樣,這是什麼原因?初學者以為這是 Python 的 bug,其實這是陷阱,函式自身也是物件,預設引數會作為該函式物件的兩個屬性存在。類似於:func.i, func.nums。函式建立之後,nums 已經有一個預設值 []
,第一次呼叫時,相當於 func.nums.append(1), 第二次呼叫相當於 func.nums.append(2),因為串列是可變物件,所以,每呼叫一次,就往串列裡面增加了一個元素。
正確的實現方法是:
def func(i=0, nums=None):
if nums is None:
nums = []
nums.append(i)
print(i)
print(nums)
func(i=1)
func(i=1)
5、 不使用串列推導式
將一個串列中的所有元素做平方處理,普通的做法就是新建一個串列,逐個迭代計算出每個值的平方,再加入到新陣列中。喜歡裝X的可能會把程式碼寫的巨難懂,各種技巧都給你用上,最後只為實現一個簡單的需求。90% 的情況下,串列推導式可以代替 map、filter 函式。
普通寫法
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]
6、不使用 enumerate 函式獲取串列元素下標索引
在某些場景,不可避免需要用到串列元素中的下標索引位置,一般程式員的做法就是按照最原始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)
7、使用 from xxx import *
偷懶的程式員喜歡直接 import * ,雖然能節省一些程式碼,但是 import *裡面隱藏著一些潛在的危險。如果 a,b 兩個包裡面都有一個叫做 foo 的模組,那麼其中一個就會被改寫。正確的做法就是顯示地把需要用到的模組 import 進來,如果遇到重名的模組,則用 as 將其重新命名。
from a import foo as foo_a
from b import foo as foo_b
8、使用Python2.X
Python2.7 在2020年官方不再維護,現在都2019年了,所以如果你的系統特別是新系統還用Python2.7的話,不失為最差的開發實踐,所以,趕緊升級到Python3.6吧。