引言
本篇部落格為譯文,翻譯自Learning Python一書作者總結的Python程式設計者共性錯誤一文,原文英文網址為
http://www.onlamp.com/pub/a/python/2004/02/05/learn_python.html
第一次翻譯這種技術部落格,有錯誤之處請及時指出。
注意:不是按照原文一字一句翻譯的,意譯,是以隻要保證技術細節對就行。
程式運作錯誤(Progmatic Mistakes)
在具體到具體程式文法之前,初學者可能遇到的錯誤,主要集中在運作程式可能會犯的一些錯誤。
在互動指令行輸入python代碼
差別系統指令行與python互動指令行的差別,在windows或linux系統指令行中輸入“python”,回車後進入python 互動指令行(interactive prompt),此時指令行以“>>>”開頭,可以輸入python代碼,不可以直接輸入ls,emac,vim等系統指令,當然此時也可以調用這些指令,但不能直接調用,要是用python的package,“import os.system”,之後才能調用;在python互動指令行也不能直接運作python檔案,在系統指令行可以,“python filename”即可,而在python互動指令行下,需要使用“import file”指令才能使用。
在檔案中必須使用print語句
在互動指令行自動列印變量或表達式結果,但是在以檔案形式運作python程式時(程式寫在代碼檔案裡),必須輸入print語句才能列印結果。
在Windows系統中注意自動擴充名
在Windows系統中使用記事本等編輯檔案中,預設儲存為txt檔案,此時使python解釋器無法運作python代碼,注意選擇儲存格式為all files,然後手動敲入檔案名加檔案擴充名,如“filename.py”,也可以使用特定的代碼編輯軟甲或內建開發環境。
Windows系統直接點選運作代碼檔案
Windows系統中輕按兩下運作代碼檔案,如果沒有input()等語句,則程式運作時會一閃而過,基本無法看到運作結果,此時,要在系統指令行下python filename.py或import module或在內建開發環境中運作程式。
import隻在第一次起作用
import語句用于導入其他子產品,但如果你一直在輸入程式,而沒有關閉互動指令行,此前import的子產品一直駐留在記憶體中,你對其他子產品的更改不會作用到目前輸入的程式中,需要重新載入(reload)子產品,具體調用reload函數,如“ reload(module)”
在互動指令行中空白行有意義
在檔案中,空白行沒有任何意義,被解釋器忽略,但是在互動指令行中,為了區分代碼塊是否完結,需要空白行來間隔其他代碼來表示,是以對于for,while,等代碼塊結束時,需要額外輸入至少一行空白行表示該代碼塊結束。
編碼錯誤(Coding Mistakes)
不要忘記冒号
if、while、for等複合語句需要加冒号。
初始化變量
在python中,隻有給一個變量指定一個值,才能在表達式中調用它,防止變量歧義問題,如預設指派到底該指派什麼0,None,“”,[] ?,這是因為python是非強類型語言,必須通過指派确定變量如何配置設定記憶體。
從第一列開始
頂層代碼,都從最左側第一列開始。
縮進一緻
避免空格與Tab混合進行縮進。
調用函數總是使用括号文法
調用函數,為函數名加括号。
在import時不要加擴充名或路徑
import時會在環境變量指定的路徑下查找,是以不需要指定路徑;import時不需要加檔案擴充名。
不要在python中輸入C代碼
- if,for,while 語句中不要加括号。
- 不要以分号結束語句
- 在while循環測試中,不能出現指派語句
程式設計錯誤(Programming mistakes)
這部分設計資料類型、函數、子產品、類等錯誤。
檔案打開不調用子產品搜尋路徑
使用檔案打開函數時,不使用子產品搜尋的路徑,而是參數給出的絕對目前目錄相對路徑。
方法是類型特定的
list的方法不能用于strings類型資料;len函數可以通用于任何帶長度對象。
不可變類型不能原地改變
不可變類型如元祖、字元串不能原地改變。如
T = (1, 2, 3)
T[2] = 4 # Error
使用簡單for循環代替while或range
當需要周遊序列對象是,直接使用for循環,而不是使用基于while或range的循環,避免使用range函數,除非必要,讓python自己處理索引,如
S = 'lumberjack'
for c in S: # simplest
print(c)
for i in range(len(S)): # too much
print(S[i])
i = 0
while i < len(S): # too much
print(S[i])
i += 1
不要指望來自函數的結果改變對象
原地改變操作如list.append()和list.sort()改變對象,但是沒有傳回被修改的對象;如
mylist = mylist.append(X)
mylist将會被指派為None而不是給修改的list。
D = {1:'a', 2:'b'}
for k in D.keys().sort():
print(D[k])
上面代碼出錯,因為sort()函數傳回None,不是序列變量不能進行循環周遊,正确的為
Ks = D.keys()
Ks.sort()
for k in Ks:
print(D[k])
轉換隻發生在數字類型之間
預設自動轉換隻發生在數字類型之間,如果在數字類型與字元串類型則不能轉換,這是因為非數字類型之間轉換無法确定轉為哪一種類型。
Cyclic資料結構可導緻循環
集合對象包含對自身的引用稱為cyclic object,python會列印為[ … ]當它發現對象存在循環時,而不是陷入無限循環。
指派産生引用,不是拷貝
這個python的核心概念。
L = [1, 2, 3]
M = ['M', L, 'Y']
print(M) # ['X', [1, 2, 3], 'Y']
L[1] = 0
print(M) # ['X', [1, 0, 3], 'Y']
可以拷貝避免共享對象。
L = [1, 2, 3]
M = ['M', L[:], 'Y']
print(M) # ['X', [1, 2, 3], 'Y']
L[1] = 0
print(L) # [1, 0, 3]
print(M) # ['X', [1, 2, 3], 'Y']
局部變量被靜态發現
python将函數内部指派的變量預設為局部變量,存在于函數範圍内,隻在函數運作時。python靜态發現局部變量。
X = 99
def func():
print(X) # Does not yet exit
X = 88 # Make X local in entire def
func() # Error!
會報錯,編譯這段代碼時,python發現指派語句,X在函數内部局部變量,實際函數運作時,指派語句還沒執行,python産生未定義名稱錯誤(undefined name error)。
上述代碼是歧義的:你是要列印全局變量X,然後建立一個局部變量X,還是這是一個程式設計錯誤?如果你要列印全局變量X,應該用global聲明或通過子產品名調用。
預設或可變對象
預設參數被存儲一次,當def語句運作時,而不是每次調用,在改變可變對象時必須小心,如
def saver(x=[]):
x.append(1)
print(x)
saver([2]) # [2, 1], Default not used
saver() # [1], Default used
saver() # [1, 1], Grows on each call!
saver() # [1, 1, 1]
改變上述行為,可以通過在函數開始處拷貝預設值或移動預設值表達式到函數體中,隻要保證指派代碼每次函數調用時執行即可。
def saver(x=None):
if x is None: # No arg passed?
x = [] # Changes new list
x.append(1)
print(x)
saver([2]) # [2, 1], Default not used
saver() # [1], Default used
saver() # [1], Doesn't grows now
saver() # [1]
其他錯誤
- 檔案從上到下讀取,是以非遞歸代碼調用在定義之下
- reload不作用于from語句
- 多繼承時從左至右,最左類繼承如果後面還有同名出現
- 空except語句捕獲所有類型異常
- Bunnies can be more dangerous than they seem(不知道如何翻譯)