Python程式設計者共性錯誤

fjie發表於2017-05-11

引言

本篇部落格為譯文,翻譯自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(不知道如何翻譯)


相關文章