Python 2.x 陷阱和地雷
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/530530/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Python 2.x gotchas and landmines
提问by David
The purpose of my question is to strengthen my knowledge base with Python and get a better picture of it, which includes knowing its faults and surprises. To keep things specific, I'm only interested in the CPython interpreter.
我的问题的目的是加强我的 Python 知识库并更好地了解它,包括了解它的缺点和惊喜。为了保持具体,我只对 CPython 解释器感兴趣。
I'm looking for something similar to what learned from my PHP landminesquestion where some of the answers were well known to me but a couple were borderline horrifying.
我正在寻找类似于从我的PHP 地雷问题中学到的东西,其中一些答案对我来说是众所周知的,但有一些答案是可怕的。
Update: Apparently one maybe two people are upset that I asked a question that's already partially answered outside of Stack Overflow. As some sort of compromise here's the URL http://www.ferg.org/projects/python_gotchas.html
更新:显然,可能有两个人对我提出的问题感到不安,而这个问题已经在 Stack Overflow 之外得到了部分回答。作为某种妥协,这里是 URL http://www.ferg.org/projects/python_gotchas.html
Note that one or two answers here already are original from what was written on the site referenced above.
请注意,这里的一两个答案已经是上述网站上所写内容的原创。
回答by Garth Kidd
Expressions in default arguments are calculated when the function is defined, notwhen it's called.
默认参数中的表达式是在定义函数时计算的,而不是在调用时计算的。
Example:consider defaulting an argument to the current time:
示例:考虑将参数默认为当前时间:
>>>import time
>>> def report(when=time.time()):
... print when
...
>>> report()
1210294387.19
>>> time.sleep(5)
>>> report()
1210294387.19
The when
argument doesn't change. It is evaluated when you define the function. It won't change until the application is re-started.
该when
参数不改变。它在您定义函数时进行评估。在重新启动应用程序之前它不会改变。
Strategy:you won't trip over this if you default arguments to None
and then do something useful when you see it:
策略:如果您将默认参数设置为None
,然后在看到它时做一些有用的事情,您就不会绊倒它:
>>> def report(when=None):
... if when is None:
... when = time.time()
... print when
...
>>> report()
1210294762.29
>>> time.sleep(5)
>>> report()
1210294772.23
Exercise:to make sure you've understood: why is this happening?
练习:确保您已经理解:为什么会发生这种情况?
>>> def spam(eggs=[]):
... eggs.append("spam")
... return eggs
...
>>> spam()
['spam']
>>> spam()
['spam', 'spam']
>>> spam()
['spam', 'spam', 'spam']
>>> spam()
['spam', 'spam', 'spam', 'spam']
回答by DzinX
You should be aware of how class variables are handled in Python. Consider the following class hierarchy:
您应该了解 Python 中如何处理类变量。考虑以下类层次结构:
class AAA(object):
x = 1
class BBB(AAA):
pass
class CCC(AAA):
pass
Now, check the output of the following code:
现在,检查以下代码的输出:
>>> 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
Surprised? You won't be if you remember that class variables are internally handled as dictionaries of a class object. For read operations, if a variable name is not found in the dictionary of current class, the parent classes are searched for it. So, the following code again, but with explanations:
惊讶?如果您还记得类变量在内部作为类对象的字典进行处理,那么您就不会这样了。对于读操作,如果在当前类的字典中没有找到变量名,则搜索父类。因此,再次使用以下代码,但有说明:
# 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
Same goes for handling class variables in class instances (treat this example as a continuation of the one above):
处理类实例中的类变量也是如此(将此示例视为上述示例的延续):
>>> 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
回答by Richard Levasseur
Loops and lambdas (or any closure, really): variables are bound by name
循环和 lambdas(或任何闭包,真的):变量由名称绑定
funcs = []
for x in range(5):
funcs.append(lambda: x)
[f() for f in funcs]
# output:
# 4 4 4 4 4
A work around is either creating a separate function or passing the args by name:
解决方法是创建一个单独的函数或按名称传递参数:
funcs = []
for x in range(5):
funcs.append(lambda x=x: x)
[f() for f in funcs]
# output:
# 0 1 2 3 4
回答by Algorias
Dynamic binding makes typos in your variable names surprisingly hard to find. It's easy to spend half an hour fixing a trivial bug.
动态绑定使您的变量名称中的拼写错误难以发现。花半个小时修复一个小错误很容易。
EDIT: an example...
编辑:一个例子...
for item in some_list:
... # lots of code
... # more code
for tiem in some_other_list:
process(item) # oops!
回答by Sven Marnach
One of the biggest surprises I ever had with Python is this one:
我在 Python 中遇到的最大惊喜之一就是这个:
a = ([42],)
a[0] += [43, 44]
This works as one might expect, except for raising a TypeError after updating the first entry of the tuple! So a
will be ([42, 43, 44],)
after executing the +=
statement, but there will be an exception anyway. If you try this on the other hand
除了在更新元组的第一个条目后引发 TypeError 之外,这正如人们所期望的那样工作!因此,a
将([42, 43, 44],)
在执行后+=
声明,但会有一个例外呢。如果你在另一方面尝试这个
a = ([42],)
b = a[0]
b += [43, 44]
you won't get an error.
你不会得到错误。
回答by user537122
try:
int("z")
except IndexError, ValueError:
pass
reason this doesn't work is because IndexError is the type of exception you're catching, and ValueError is the name of the variable you're assigning the exception to.
这不起作用的原因是 IndexError 是您要捕获的异常类型,而 ValueError 是您将异常分配给的变量的名称。
Correct code to catch multiple exceptions is:
捕获多个异常的正确代码是:
try:
int("z")
except (IndexError, ValueError):
pass
回答by Tom Dunham
There was a lot of discussion on hidden language features a while back: hidden-features-of-python. Where some pitfalls were mentioned (and some of the good stuff too).
前一段时间有很多关于隐藏语言特性的讨论:hidden-features-of-python。提到了一些陷阱(以及一些好东西)。
Also you might want to check out Python Warts.
此外,您可能想查看Python Warts。
But for me, integer division's a gotcha:
但对我来说,整数除法是一个问题:
>>> 5/2
2
You probably wanted:
你可能想要:
>>> 5*1.0/2
2.5
If you really want this (C-like) behaviour, you should write:
如果你真的想要这种(类似 C 的)行为,你应该写:
>>> 5//2
2
As that will work with floats too (and it will work when you eventually go to Python 3):
因为这也适用于浮点数(当您最终使用Python 3时它会起作用):
>>> 5*1.0//2
2.0
GvR explains how integer division came to work how it does on the history of Python.
GvR 解释了整数除法如何在Python 的历史中起作用。
回答by Viktiglemma
List slicinghas caused me a lot of grief. I actually consider the following behavior a bug.
列表切片让我很伤心。我实际上认为以下行为是一个错误。
Define a list x
定义一个列表 x
>>> x = [10, 20, 30, 40, 50]
Access index 2:
访问索引2:
>>> x[2]
30
As you expect.
如你所料。
Slice the list from index 2 and to the end of the list:
将列表从索引 2 切片到列表末尾:
>>> x[2:]
[30, 40, 50]
As you expect.
如你所料。
Access index 7:
访问索引7:
>>> x[7]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
Again, as you expect.
再次,如您所料。
However, try to slice the list from index 7 until the end of the list:
但是,尝试将列表从索引 7 切片到列表末尾:
>>> x[7:]
[]
???
???
The remedy is to put a lot of tests when using list slicing. I wish I'd just get an error instead. Much easier to debug.
补救方法是在使用列表切片时进行大量测试。我希望我只是得到一个错误。更容易调试。
回答by Jason Baker
Not including an __init__
.py in your packages. That one still gets me sometimes.
包中不包含__init__
.py。那个有时仍然让我着迷。
回答by David
The only gotcha/surprise I've dealt with is with CPython's GIL. If for whatever reason you expect python threads in CPython to run concurrently... well they're not and this is pretty well documented by the Python crowd and even Guido himself.
我处理过的唯一问题/惊喜是使用 CPython 的 GIL。如果出于某种原因,您希望 CPython 中的 Python 线程同时运行……那么它们不是,Python 人群甚至 Guido 本人对此都有很好的记录。
A long but thorough explanation of CPython threading and some of the things going on under the hood and why true concurrency with CPython isn't possible. http://jessenoller.com/2009/02/01/python-threads-and-the-global-interpreter-lock/
对 CPython 线程和一些幕后发生的事情以及为什么不可能与 CPython 实现真正的并发的详细解释。 http://jessenoller.com/2009/02/01/python-threads-and-the-global-interpreter-lock/