可能重复:
“Least Astonishment” in Python: The Mutable Default Argument
好的,基本上我有这个代码:
# Our Rule type.
class Rule(object):
"""An object to represent a rule and include
methods to apply these rules."""
def __init__(self,bind="F",rule=["F"]):
self.bind = bind
self.rule = rule
def show(self):
try:
print self.bind
#print self.rule
for i in range(len(self.rule)):
print self.rule[i]
except:
print "Incomplete"
def inflate(self,seq):
for i in range(len(seq)):
if seq[i] == self.bind:
seq[i] = self.rule
elif type(seq) == type(["X"]):
seq[i] = self.inflate(seq[i])
return seq
def inflate_depth(self,seq,depth):
while depth > 0:
seq = self.inflate(seq)
depth = depth - 1
print self.rule
return seq
我用其代码从另一个文件中调用它:
pity = Rule(rule=["R","F","L","F","L","F","R","F"])
seq = ["F"]
inf = pity.inflate_depth(seq,2)
所以,我最终应该得到一个如下所示的列表:
[['R', [...], 'L', [...], 'L', [...], 'R', [...]]]
这似乎工作正常,但有一个基本错误。
self.rule
已更改为包含['R', [...], 'L', [...], 'L', [...], 'R', [...]]
为什么呢?我没有为该变量分配新值,但它仍会发生变化。
答案 0 :(得分:2)
默认参数在定义函数时计算一次,稍后重复使用。
def f(x, a = [])
a.append(x)
return a
print f(0)
print f(1)
打印
[0]
[0, 1]
避免此问题的常用方法是使用None
作为默认参数:
def f(x, a = None):
if a is None:
a = []
# ...
有关详细说明,请参阅Python tutorial。
答案 1 :(得分:2)
我不确定这是不是问题,但你必须非常小心在这样的函数中设置默认列表:
def __init__(self,bind="F",rule=["F"]):
self.bind = bind
self.rule = rule
每次调用此函数时,每次都会使用相同的列表!因此,每次在一个Rule对象中修改self.rule
时,都会在所有其他对象中修改它。
相反,你应该这样做:
def __init__(self,bind="F",rule=None):
self.bind = bind
if not rule:
self.rule = ['F']
else:
self.rule = rule
这样,您每次都可以创建一个新的列表对象,而不是重复使用相同的列表对象。
例如:
class foo:
def __init__(self, x=[]):
self.x = x
self.x.append(3)
d = foo()
e = foo()
f = foo()
print f.x # prints [3,3,3]
答案 2 :(得分:2)
我对此并不是100%肯定,但我怀疑问题是Python中的变量是引用而不是值。也就是说,当您设置seq[i] = self.rule
时,seq[i]
指向self.rule
存储在内存中的位置,对seq[i]
的值所做的任何更改都会导致更改为{ {1}}因为它们的值存储在内存中的相同位置。
你可能应该做的是将self.rule
的深度复制到self.rule
,而不是简单地分配它,以便他们使用两个不同的内存地址来存储它们的值(参见http://docs.python.org/library/copy.html for更多信息),分配给seq[i]
的方式不会影响seq[i]
。
答案 3 :(得分:1)
它会更改,因为rule
是列表中的引用。然后,您可以指定seq[i]
来引用self.rule
指向的对象。因此,当您修改seq
时,您正在修改self.rule引用的同一对象。
>>> a = [1,2,3,4]
>>> b = a
>>> b.append(5)
>>> print a
[1, 2, 3, 4, 5]
我相信您要找的是rule
中存储的列表的副本。这可以通过几种方式完成。在第一个中,您从当前可迭代构造一个新列表。
seq[i] = list(self.rule) # Makes a new list that is a copy of self.rule
如果self.rule
包含不可变的列表(整数,字符串等),那么这应该足够了。但是,如果self.rule
包含类似实例的可变项,那么您最终会得到一个新列表,它引用self.rule
中引用的所有相同对象。这意味着可以在以下代码中看到:
>>> class A(object):
... def __init__(self, val):
... self.x = val
>>> a = [A(i) for i in xrange(10)]
>>> print a[0].x
0
>>> print a[1].x
1
>>> b = list(a)
>>> b.append(A(10))
>>> print b[0].x
0
>>> b[0].x = 99
>>> print b[0].x
99
>>> print a[0].x
99
>>> print (len(a), len(b))
(10, 11)
这里我们有两个不同的列表,但两个不同的列表都有10个共同的引用。列表b
还有一个额外的参考,进一步巩固了它的区别。要避免该问题,您可以使用第二种方法deepcopy
。
>>> from copy import deepcopy
>>> a = [A(i) for i in xrange(10)]
>>> b = deepcopy(a)
>>> b[0].x = 99
>>> print b[0].x
99
>>> print a[0].x
0
这会生成列表的副本以及该列表中的所有对象。
最后,我要提醒您不要使用列表作为rule
关键字参数的默认值。这几乎肯定不会像你期望的那样发挥作用。关键字参数rule
在定义中获取列表["F"]
的实例。对__init__
的所有调用都将获得相同的列表实例,因此修改该列表会影响rule
更改时的默认行为。有关其他示例,请参阅Default value of memberfunction。