引言
本篇博客为译文,翻译自Learning Python一书作者总结的Python编程者共性错误一文,原文英文网址为
第一次翻译这种技术博客,有错误之处请及时指出。
注意:不是按照原文一字一句翻译的,意译,因此只要保证技术细节对就行。
程序运行错误(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(不知道如何翻译)