Python 中的常见陷阱
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1011431/
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
Common pitfalls in Python
提问by Anurag Uniyal
Possible Duplicate:
Python 2.x gotcha’s and landmines
可能的重复:
Python 2.x 陷阱和地雷
Today I was bitten again by mutable default arguments after many years. I usually don't use mutable default arguments unless needed, but I think with time I forgot about that. Today in the application I added tocElements=[] in a PDF generation function's argument list and now "Table of Contents" gets longer and longer after each invocation of "generate pdf". :)
多年后,今天我再次被可变的默认参数咬住了。除非需要,我通常不使用可变的默认参数,但我想随着时间的推移我忘记了这一点。今天在应用程序中,我在 PDF 生成函数的参数列表中添加了 tocElements=[],现在每次调用“生成 pdf”后“目录”都会变得越来越长。:)
What else should I add to my list of things to MUST avoid?
我还应该在我必须避免的事情列表中添加什么?
Always import modules the same way, e.g.
from y import x
andimport x
are treated as different modules.Do not use rangein place of lists because
range()
will become an iterator anyway, the following will fail:myIndexList = [0, 1, 3] isListSorted = myIndexList == range(3) # will fail in 3.0 isListSorted = myIndexList == list(range(3)) # will not
Same thing can be mistakenly done with xrange:
myIndexList == xrange(3)
Be careful catching multiple exception types:
try: raise KeyError("hmm bug") except KeyError, TypeError: print TypeError
This prints "hmm bug", though it is not a bug; it looks like we are catching exceptions of both types, but instead we are catching KeyError only as variable TypeError,use this instead:
try: raise KeyError("hmm bug") except (KeyError, TypeError): print TypeError
始终以相同的方式导入模块,例如
from y import x
和import x
被视为不同的模块。不要使用范围代替列表,因为
range()
无论如何都会成为迭代器,以下将失败:myIndexList = [0, 1, 3] isListSorted = myIndexList == range(3) # will fail in 3.0 isListSorted = myIndexList == list(range(3)) # will not
使用xrange可能会错误地做同样的事情:
myIndexList == xrange(3)
小心捕捉多种异常类型:
try: raise KeyError("hmm bug") except KeyError, TypeError: print TypeError
这会打印“hmm bug”,尽管它不是错误;看起来我们正在捕获两种类型的异常,但我们只将 KeyError 作为变量TypeError捕获,而是使用它:
try: raise KeyError("hmm bug") except (KeyError, TypeError): print TypeError
回答by e-satis
Don't use index to loop over a sequence
不要使用索引来循环序列
Don't :
别 :
for i in range(len(tab)) :
print tab[i]
Do :
做 :
for elem in tab :
print elem
Forwill automate most iteration operations for you.
For将为您自动化大多数迭代操作。
Use enumerate
if you really need both the index and the element.
使用enumerate
如果你真的需要这两个指标和元素。
for i, elem in enumerate(tab):
print i, elem
Be careful when using "==" to check against Trueor False
使用“==”检查True或False时要小心
if (var == True) :
# this will execute if var is True or 1, 1.0, 1L
if (var != True) :
# this will execute if var is neither True nor 1
if (var == False) :
# this will execute if var is False or 0 (or 0.0, 0L, 0j)
if (var == None) :
# only execute if var is None
if var :
# execute if var is a non-empty string/list/dictionary/tuple, non-0, etc
if not var :
# execute if var is "", {}, [], (), 0, None, etc.
if var is True :
# only execute if var is boolean True, not 1
if var is False :
# only execute if var is boolean False, not 0
if var is None :
# same as var == None
Do not check if you can, just do it and handle the error
不要检查是否可以,只需执行并处理错误
Pythonistas usually say "It's easier to ask for forgiveness than permission".
Pythonistas 通常会说“请求原谅比许可更容易”。
Don't :
别 :
if os.path.isfile(file_path) :
file = open(file_path)
else :
# do something
Do :
做 :
try :
file = open(file_path)
except OSError as e:
# do something
Or even better with python 2.6+ / 3:
甚至使用 python 2.6+ / 3 更好:
with open(file_path) as file :
It is much better because it's much more generical. You can apply "try / except" to almost anything. You don't need to care about what to do to prevent it, just about the error you are risking.
它要好得多,因为它更通用。您几乎可以将“尝试/除外”应用于任何事物。您无需关心如何防止它发生,只需关心您所冒的错误。
Do not check against type
不检查类型
Python is dynamically typed, therefore checking for type makes you lose flexibility. Instead, use duck typing by checking behavior. E.G, you expect a string in a function, then use str() to convert any object in a string. You expect a list, use list() to convert any iterable in a list.
Python 是动态类型的,因此检查类型会使您失去灵活性。相反,通过检查行为来使用鸭子类型。EG,您希望函数中有一个字符串,然后使用 str() 转换字符串中的任何对象。您需要一个列表,请使用 list() 转换列表中的任何可迭代对象。
Don't :
别 :
def foo(name) :
if isinstance(name, str) :
print name.lower()
def bar(listing) :
if isinstance(listing, list) :
listing.extend((1, 2, 3))
return ", ".join(listing)
Do :
做 :
def foo(name) :
print str(name).lower()
def bar(listing) :
l = list(listing)
l.extend((1, 2, 3))
return ", ".join(l)
Using the last way, foo will accept any object. Bar will accept strings, tuples, sets, lists and much more. Cheap DRY :-)
使用最后一种方式, foo 将接受任何对象。Bar 将接受字符串、元组、集合、列表等等。便宜的干:-)
Don't mix spaces and tabs
不要混用空格和制表符
Just don't. You would cry.
只是不要。你会哭的。
Use objectas first parent
使用对象作为第一个父对象
This is tricky, but it will bite you as your program grows. There are old and new classes in Python 2.x. The old ones are, well, old. They lack some features, and can have awkward behavior with inheritance. To be usable, any of your class must be of the "new style". To do so, make it inherit from "object" :
这很棘手,但随着程序的增长,它会咬你。Python 2.x 中有新旧类。旧的,好吧,旧的。它们缺乏一些特性,并且在继承时可能会有尴尬的行为。为了可用,您的任何课程都必须是“新风格”。为此,请使其继承自 "object" :
Don't :
别 :
class Father :
pass
class Child(Father) :
pass
Do :
做 :
class Father(object) :
pass
class Child(Father) :
pass
In Python 3.x all classes are new style so you can declare class Father:
is fine.
在 Python 3.x 中,所有类都是新样式,因此您可以声明class Father:
很好。
Don't initialize class attributes outside the __init__
method
不要在__init__
方法之外初始化类属性
People coming from other languages find it tempting because that what you do the job in Java or PHP. You write the class name, then list your attributes and give them a default value. It seems to work in Python, however, this doesn't work the way you think.
来自其他语言的人发现它很诱人,因为你用 Java 或 PHP 做的工作。你写下类名,然后列出你的属性并给它们一个默认值。它似乎在 Python 中工作,但是,这并不像您想象的那样工作。
Doing that will setup class attributes (static attributes), then when you will try to get the object attribute, it will gives you its value unless it's empty. In that case it will return the class attributes.
这样做将设置类属性(静态属性),然后当您尝试获取对象属性时,它将为您提供其值,除非它为空。在这种情况下,它将返回类属性。
It implies two big hazards :
这意味着两大危害:
- If the class attribute is changed, then the initial value is changed.
- If you set a mutable object as a default value, you'll get the same object shared across instances.
- 如果类属性改变,则初始值改变。
- 如果您将可变对象设置为默认值,您将获得跨实例共享的相同对象。
Don't (unless you want static) :
不要(除非你想要静态):
class Car(object):
color = "red"
wheels = [wheel(), Wheel(), Wheel(), Wheel()]
Do :
做 :
class Car(object):
def __init__(self):
self.color = "red"
self.wheels = [wheel(), Wheel(), Wheel(), Wheel()]
回答by Jorisslob
When you need a population of arrays you might be tempted to type something like this:
当您需要大量数组时,您可能会想键入以下内容:
>>> a=[[1,2,3,4,5]]*4
And sure enough it will give you what you expect when you look at it
果然,当你看到它时,它会给你你所期望的
>>> from pprint import pprint
>>> pprint(a)
[[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
But don't expect the elements of your population to be seperate objects:
但不要指望您的人口元素是单独的对象:
>>> a[0][0] = 2
>>> pprint(a)
[[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5]]
Unless this is what you need...
除非这是你需要的......
It is worth mentioning a workaround:
值得一提的是一个解决方法:
a = [[1,2,3,4,5] for _ in range(4)]
回答by S.Lott
Python Language Gotchas -- things that fail in very obscure ways
Python 语言陷阱——以非常晦涩的方式失败的事情
Using mutable default arguments.
Leading zeroes mean octal.
09
is a very obscure syntax error in Python 2.xMisspelling overridden method names in a superclass or subclass. The superclass misspelling mistake is worse, because none of the subclasses override it correctly.
使用可变的默认参数。
前导零表示八进制。
09
是 Python 2.x 中一个非常晦涩的语法错误在超类或子类中拼写错误覆盖的方法名称。超类拼写错误更糟,因为没有一个子类正确覆盖它。
Python Design Gotchas
Python 设计陷阱
Spending time on introspection (e.g. trying to automatically determine types or superclass identity or other stuff). First, it's obvious from reading the source. More importantly, time spent on weird Python introspection usually indicates a fundamental failure to grasp polymorphism. 80% of the Python introspection questions on SO are failure to get Polymorphism.
Spending time on code golf. Just because your mental model of your application is four keywords ("do", "what", "I", "mean"), doesn't mean you should build a hyper-complex introspective decorator-driven framework to do that. Python allows you to take DRY to a level that is silliness. The rest of the Python introspection questions on SO attempts to reduce complex problems to code golf exercises.
Monkeypatching.
Failure to actually read through the standard library, and reinventing the wheel.
Conflating interactive type-as-you go Python with a proper program. While you're typing interactively, you may lose track of a variable and have to use
globals()
. Also, while you're typing, almost everything is global. In proper programs, you'll never "lose track of" a variable, and nothing will be global.
花时间进行内省(例如,尝试自动确定类型或超类身份或其他内容)。首先,从阅读源代码可以看出这一点。更重要的是,花在奇怪的 Python 内省上的时间通常表明根本无法掌握多态性。关于 SO 的 Python 内省问题 80% 都未能得到多态性。
花时间在代码高尔夫上。仅仅因为您的应用程序的心智模型是四个关键字(“do”、“what”、“I”、“mean”),并不意味着您应该构建一个超复杂的内省装饰器驱动框架来做到这一点。Python 允许您将 DRY 带到一个愚蠢的水平。关于 SO 的 Python 内省问题的其余部分试图将复杂的问题简化为编码高尔夫练习。
猴子补丁。
未能真正通读标准库,并重新发明轮子。
使用适当的程序将 Python 中的交互式类型随用随走。当您以交互方式输入时,您可能会丢失对变量的跟踪,而必须使用
globals()
. 此外,在您打字时,几乎所有内容都是全局的。在适当的程序中,您永远不会“忘记”变量,并且没有任何东西是全局的。
回答by Will Harris
Mutating a default argument:
改变默认参数:
def foo(bar=[]):
bar.append('baz')
return bar
The default value is evaluated only once, and not every time the function is called. Repeated calls to foo()
would return ['baz']
, ['baz', 'baz']
, ['baz', 'baz', 'baz']
, ...
默认值只计算一次,而不是每次调用函数时。重复调用foo()
将返回['baz']
, ['baz', 'baz']
, ['baz', 'baz', 'baz']
, ...
If you want to mutate bar do something like this:
如果你想改变 bar 做这样的事情:
def foo(bar=None):
if bar is None:
bar = []
bar.append('baz')
return bar
Or, if you like arguments to be final:
或者,如果你喜欢最后的论点:
def foo(bar=[]):
not_bar = bar[:]
not_bar.append('baz')
return not_bar
回答by Jochen Walter
I don't know whether this is a common mistake, but while Python doesn't have increment and decrement operators, double signs are allowed, so
我不知道这是否是一个常见的错误,但是虽然 Python 没有自增和自减运算符,但允许使用双号,所以
++i
and
和
--i
is syntactically correct code, but doesn't do anything "useful" or that you may be expecting.
是语法正确的代码,但没有做任何“有用”或您可能期望的事情。
回答by Will Harris
Rolling your own code before looking in the standard library. For example, writing this:
在查看标准库之前滚动您自己的代码。例如,写这个:
def repeat_list(items):
while True:
for item in items:
yield item
When you could just use this:
当你可以使用这个时:
from itertools import cycle
Examples of frequently overlooked modules (besides itertools
) include:
经常被忽视的模块示例(除了itertools
)包括:
optparse
for creating command line parsersConfigParser
for reading configuration files in a standard mannertempfile
for creating and managing temporary filesshelve
for storing Python objects to disk, handy when a full fledged database is overkill
optparse
用于创建命令行解析器ConfigParser
以标准方式读取配置文件tempfile
用于创建和管理临时文件shelve
用于将 Python 对象存储到磁盘,当成熟的数据库过度使用时很方便
回答by uolot
Avoid using keywords as your own identifiers.
避免使用关键字作为您自己的标识符。
Also, it's always good to not use from somemodule import *
.
此外,不使用from somemodule import *
.
回答by Stefano Borini
Surprised that nobody said this:
没想到没人这么说:
Mix tab and spaces when indenting.
缩进时混合制表符和空格。
Really, it's a killer. Believe me. In particular, if it runs.
真的,这是一个杀手。相信我。特别是,如果它运行。
回答by Imagist
Not using functional tools. This isn't just a mistake from a style standpoint, it's a mistake from a speed standpoint because a lot of the functional tools are optimized in C.
不使用功能性工具。这不仅仅是从风格的角度来看是一个错误,从速度的角度来看也是一个错误,因为很多功能工具都是用 C 优化的。
This is the most common example:
这是最常见的例子:
temporary = []
for item in itemlist:
temporary.append(somefunction(item))
itemlist = temporary
The correct way to do it:
正确的做法:
itemlist = map(somefunction, itemlist)
The just as correct way to do it:
同样正确的方法:
itemlist = [somefunction(x) for x in itemlist]
And if you only need the processed items available one at a time, rather than all at once, you can save memory and improve speed by using the iterable equivalents
如果您一次只需要一个可用的已处理项目,而不是一次全部可用,则可以通过使用可迭代等效项来节省内存并提高速度
# itertools-based iterator
itemiter = itertools.imap(somefunction, itemlist)
# generator expression-based iterator
itemiter = (somefunction(x) for x in itemlist)
回答by rofrankel
If you're coming from C++, realize that variables declared in a class definition are static. You can initialize nonstatic members in the initmethod.
如果您来自 C++,请意识到在类定义中声明的变量是静态的。您可以在init方法中初始化非静态成员。
Example:
例子:
class MyClass:
static_member = 1
def __init__(self):
self.non_static_member = random()