我正在寻找类似于我从PHP地雷问题中学到的内容,其中一些答案对我来说很熟悉,但有几个答案令人毛骨悚然。
更新: 显然有一两个人因为我在Stack Overflow之外提出了一个部分回答的问题而感到不满。作为某种妥协,这是URL http://www.ferg.org/projects/python_gotchas.html 请注意,这里已经有一两个答案是原创的,来源于上述网站的内容。
默认参数中的表达式在函数定义时计算,而不是在调用时计算。
例如:考虑将一个参数默认值设置为当前时间:
>>>import time
>>> def report(when=time.time()):
... print when
...
>>> report()
1210294387.19
>>> time.sleep(5)
>>> report()
1210294387.19
when
参数不会改变。它在定义函数时被计算,直到应用程序重新启动之前都不会改变。
策略: 如果你将默认参数设置为 None
,并且在使用时进行一些有用的操作,就不会遇到这个问题:
>>> def report(when=None):
... if when is None:
... when = time.time()
... print when
...
>>> report()
1210294762.29
>>> time.sleep(5)
>>> report()
1210294772.23
练习:确保您已经理解:为什么会发生这种情况?
>>> def spam(eggs=[]):
... eggs.append("spam")
... return eggs
...
>>> spam()
['spam']
>>> spam()
['spam', 'spam']
>>> spam()
['spam', 'spam', 'spam']
>>> spam()
['spam', 'spam', 'spam', 'spam']
你应该了解 Python 中类变量的处理方式。考虑以下类层次结构:
class AAA(object):
x = 1
class BBB(AAA):
pass
class CCC(AAA):
pass
现在,请检查以下代码的输出结果:>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
>>> print AAA.x, BBB.x, CCC.x
3 2 3
惊讶吗?如果您记得类变量在内部被处理为类对象的字典,就不会感到惊讶了。对于读取操作,如果在当前类的字典中找不到变量名,则会在父类中搜索该变量名。因此,以下代码再次出现,但附有解释:
# AAA: {'x': 1}, BBB: {}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 1 1
>>> BBB.x = 2
# AAA: {'x': 1}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
1 2 1
>>> AAA.x = 3
# AAA: {'x': 3}, BBB: {'x': 2}, CCC: {}
>>> print AAA.x, BBB.x, CCC.x
3 2 3
处理类实例中的类变量也是同样的方式(将此示例视为上面示例的续集):
>>> a = AAA()
# a: {}, AAA: {'x': 3}
>>> print a.x, AAA.x
3 3
>>> a.x = 4
# a: {'x': 4}, AAA: {'x': 3}
>>> print a.x, AAA.x
4 3
循环和lambda(或任何闭包):变量通过名称绑定
funcs = []
for x in range(5):
funcs.append(lambda: x)
[f() for f in funcs]
# output:
# 4 4 4 4 4
解决方法是创建一个单独的函数,或通过名称传递参数:
funcs = []
for x in range(5):
funcs.append(lambda x=x: x)
[f() for f in funcs]
# output:
# 0 1 2 3 4
动态绑定使变量名的拼写错误难以发现。很容易花费半小时修复一个微不足道的错误。
编辑:一个例子...
for item in some_list:
... # lots of code
... # more code
for tiem in some_other_list:
process(item) # oops!
tiem
将被标记为未使用的变量。 - DzinxPython 给我带来的最大惊喜之一就是:
a = ([42],)
a[0] += [43, 44]
这个代码看起来很正常,但在更新元组的第一个条目后却会引发TypeError! 所以在执行 += 语句后,
a 将变为
([42, 43, 44],),但仍会抛出异常。 另一方面,如果你尝试这样做
<code><code><code>a = ([42],)
b = a[0]
b += [43, 44]
</code></code></code>
你不会得到错误。
a[0].extend([43, 44])
。 - Dzinxtry:
int("z")
except IndexError, ValueError:
pass
这个代码不能正常工作的原因是您捕获的异常类型是IndexError,而将异常赋值给变量的名称是ValueError。
正确的捕获多个异常的代码是:
try:
int("z")
except (IndexError, ValueError):
pass
之前曾有讨论过Python中的隐藏语言特性:Python的隐藏特性。其中提到了一些陷阱(也有一些好东西)。
你也许想看看Python Warts。
但对我来说,整数除法是一个坑:
>>> 5/2
2
您可能想要:
>>> 5*1.0/2
2.5
>>> 5//2
2
由于它也适用于浮点数(并且在您最终转到Python 3时也适用):
>>> 5*1.0//2
2.0
*1.0
更好。这是我的意见,因为它能够表达你真正的意图。通过乘以1.0来实现这一点有点混淆你想要的,任何不知道代码的读者可能会误以为1.0只是一个笔误(也许应该是10.0?)。 - Alfe如果你的包中不包括__init__.py
文件,有时会让人感到困惑。
列表切片给我带来了很多烦恼。实际上,我认为以下行为是一个bug。
定义一个列表x:
>>> x = [10, 20, 30, 40, 50]
访问索引为2:
>>> x[2]
30
按照您的预期。
从索引2开始切片列表并切到列表末尾:
>>> x[2:]
[30, 40, 50]
如你所预期。
访问索引7:
>>> x[7]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
如你所预期的一样。
但是,尝试从索引7开始切片到列表结尾:
>>> x[7:]
[]
解决方法是在使用列表切片时添加很多测试。我希望只是得到一个错误提示而不是什么都不说。这样更容易调试。