Python是一个解释型、面向对象、具有动态语义的高级语言。它具有高级数据结构、动态类型绑定,支持模块化。由于Python程序员往往并非只会这一门语言,受其他语言的影响,我们在编写Python代码的时候容易出现一些错误,本文中列出了10点。
一、函数参数默认值
Python允许定义函数参数的默认值,这和C/C++是一致的。但是所不同的是,解释器只对该默认参数赋值一次。
>>> def foo(bar=[]):
... bar.append("baz”)
... return bar
比如说这个foo函数,如果调用的时候自带有参数foo(a),这没什么可说的。如果用foo(),那么bar只有在一次调用无参函数foo()的时候才会执行bar=[],之后若再调用foo(),那么bar会继续使用刚才的bar,这个bar就类似于C中的static局部变量。
>>> foo()
["baz"]
>>> foo()
["baz", "baz"]
>>> foo()
["baz", "baz", "baz”]
解决办法是,在函数内部做判断。如果默认参数想定义为空的类型,用None。
>>> def foo(bar=None):
... if bar is None:# or if not bar:
... bar = []
... bar.append("baz")
... return bar
...
>>> foo()
["baz"]
>>> foo()
["baz"]
>>> foo()
["baz”]
二、类变量的继承
Python里,类变量的内部是由字典实现的。在派生类变量中,如果成员没有被定义,则会去基类中找。如果有的话的就不去看基类的了。
>>> class A(object):
... x = 1
...
>>> class B(A):
... pass
...
>>> class C(A):
... pass
…
>>> print A.x, B.x, C.x
1 1 1
>>> B.x = 2
>>> print A.x, B.x, C.x
1 2 1
>>> A.x = 3
>>> print A.x, B.x, C.x
3 2 3
三、异常处理时参数声明
异常处理时except后面不能跟多个异常。
>>> try:
... l = ["a", "b"]
... int(l[2])
... except ValueError, IndexError:
... pass
上面这么写是不对的,第二个异常情况是捕获不到的,应该像下面这样写。
>>> try:
... l = ["a", "b"]
... int(l[2])
... except (ValueError, IndexError) as e:
... pass
四、变量的作用域
C/C++里,全局变量在函数内部是完全可见的。但是在Python里,由于是解释型语言,所以不一样了。对于一个变量,如果首次出现在赋值语句的左边,那么会被认为是一个局部变量,和外面的同名全局变量没关系。如果首次出现在赋值语句右边或者其他地方,那么就会被认为是全局变量,直接用全局变量。
>>> lst = [1, 2, 3]
>>> def foo1():
... lst.append(5) # This works ok...
...
>>> foo1()
>>> lst
[1, 2, 3, 5]
>>> lst = [1, 2, 3]
>>> def foo2():
... lst += [5] # ... but this bombs!
...
>>> foo2()
这就是为什么上面两个函数,前者是正确的,后者是错误的。
五、在遍历list的时候修改它
这个错误其实在C++使用迭代器时也会出现。即使用for循环来遍历list时,由于循环之前就确定了循环次数,修改list会导致下标的变化。
>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> for i in range(len(numbers)):
... if odd(numbers[i]):
... del numbers[i]
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: list index out of range
使用下面这种高级写法,可以避免此类错误。
>>> odd = lambda x : bool(x % 2)
>>> numbers = [n for n in range(10)]
>>> numbers[:] = [n for n in numbers if not odd(n)]
>>> numbers
[0, 2, 4, 6, 8]
六、闭包中绑定变量
闭包中的变量的值是内函数
lambda x : i * x
调用的时候才确定的。
>>> def create_multipliers():
... return [lambda x : i * x for i in range(5)]
>>> for multiplier in create_multipliers():
... print multiplier(2)
对于上面的程序,不管哪一个内函数被调用时,i的值都是最后的4。因此输出会是8 8 8 8 8,而不是0 2 4 6 8。解决的办法可以利用默认参数来生成匿名函数。
>>> def create_multipliers():
... return [lambda x, i=i : i * x for i in range(5)]
...
>>> for multiplier in create_multipliers():
... print multiplier(2)
七、模块之间的循环依赖
Python解释器会去设法避免重复导入模块,但是要注意顺序。
八、小心重名的模块
Python的类库非常丰富,但是要小心重名的模块。
九、不同Python版本的坑
注意Python2和Python3的区别。
十、类的析构函数
当析构函数被调用时,解释器会把变量变为None,这样有些事情就做不了了。
import foo
class Bar(object):
...
def __del__(self):
foo.cleanup(self.myhandle)
可以使用
atexit.register来引入另一个函数做想做的事情。
import foo
import atexit
def cleanup(handle):
foo.cleanup(handle)
class Bar(object):
def __init__(self):
...
atexit.register(cleanup, self.myhandle)
本文翻译自,有做增删改:
http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make