我今天遇到了一个有趣的python bug,在这个bug中,反复实例化一个类似乎处于保持状态。在以后的实例化调用中,已经定义了变量。
我将问题归结为以下类/shell交互。我认识到这不是初始化类变量的最佳方法,但它肯定不应该这样做。这是一个真正的bug还是一个"特性"?D
测试:
class Tester():
def __init__(self):
self.mydict = self.test()
def test(self,out={}):
key ="key"
for i in ['a','b','c','d']:
if key in out:
out[key] += ','+i
else:
out[key] = i
return out
Python提示:
Python 2.6.6 (r266:84292, Oct 6 2010, 00:44:09)
[GCC 4.2.1 (Apple Inc. build 5664)] on darwin
>>> import tester
>>> t = tester.Tester()
>>> print t.mydict
{'key': 'a,b,c,d'}
>>> t2 = tester.Tester()
>>> print t2.mydict
{'key': 'a,b,c,d,a,b,c,d'}
它是否可以将out解释为一个全局变量,从而在调用test时不断地向其添加?
dupe:stackoverflow.com/questions/1132941/…
"最小惊异"和可变默认参数的可能重复
这是几乎所有的Python用户都会遇到的一个特性。主要用于缓存等,以避免重复冗长的计算(实际上是简单的记忆化),尽管我确信人们已经找到了它的其他用途。
这样做的原因是,def语句只执行一次,即在定义函数时执行一次。因此,初始值设定项值只创建一次。对于像列表或字典这样的引用类型(与不能更改的不可变类型相反),这最终会成为一个可见的、令人惊讶的陷阱,而对于值类型,则不会被注意到。
通常情况下,人们都是这样工作的:
def test(a=None):
if a is None:
a = {}
# ... etc.
你解释得很好。我想再加一行。函数是python中的一个对象,当我们第一次调用函数对象时,我们将重用同一个对象。您可以在每次通话后打印ID(测试)进行确认。您将获得相同的函数对象ID,这意味着只有一个对象正在共享。
通常,默认方法参数不应是可变的。而是这样做:
def test(self, out=None):
out = out or {}
# other code goes here.
请参阅这些链接,了解更多关于为什么需要这样做以及为什么它是Python语言的"特性"而不是bug的详细信息。
"最小惊异"和可变的默认参数
http://effbot.org/zone/default-values.htm(http://effbot.org/zone/default-values.htm)
out = out or {}不仅具有Perlish特性,而且如果调用者传入自己的空映射对象,它也会失败。最好是if out is None: out = {}。
您正在修改方法中的函数关键字参数out的值。
这篇博文简洁地解释了这一点:
expressions in default arguments are calculated when the function is defined, not when it’s called.
函数是在创建类时定义的,不是为每个实例定义的。如果您这样修改它,问题就消失了:
def test(self,out=None):
if out is None:
out = {}
key ="key"
for i in ['a','b','c','d']:
if key in out:
out[key] += ','+i
else:
out[key] = i
return out