[英]Python “strange” output
class Foo(object):
def __init__(self,x):
self.x = x
self.is_bar = False
def __repr__(self): return str(self.x)
class Bar(object):
def __init__(self,l = []):
self.l = l
def add(self,o):
self.l += [o]
def __repr__(self): return str(self.l)
def foo_plus_foo(f1,f2):
t = Bar()
if not (f1.is_bar and f2.is_bar):
f1.is_bar = True
f2.is_bar = True
t.add(f1)
t.add(f2)
print 'HERE'
return t
if __name__ == '__main__':
li = [Foo(1), Foo(2)]
print foo_plus_foo(li[0],li[1])
print foo_plus_foo(li[0],li[1])
意想不到的输出:
HERE
[1, 2]
[1, 2]
预期产量:
HERE
[1, 2]
[]
怎么了? 我做错了什么? 为什么python使用旧值? 我该怎么做才能避免这种情况?
谢谢!
决不。 做。 这个。
def __init__(self,l = []):
决不。
一个list
对象被重用。 并且它是Mutable,因此每次重用时,都会更新在方法定义中创建的唯一[]
。
总是。 做。 这个。
def __init__( self, l= None ):
if l is None: l = []
这创建了一个全新的,独特的列表实例。
您将l
定义为默认值为[]
。 这是一个经典的Python陷阱 。
class Bar(object):
def __init__(self,l = []):
在定义时而非运行时评估默认值。 它只评估一次。
所以t=Bar()
每次都将tl
设置为相同的列表。
要解决此问题,请将Bar更改为
class Bar(object):
def __init__(self,l = None):
if l is None:
l=[]
self.l = l
def add(self,o):
self.l += [o]
def __repr__(self): return str(self.l)
罪魁祸首是Bar类定义中定义的l = [] 。 此类列表在类定义期间实例化一次,并用作默认值。 超级危险!! 我和其他许多人被这个人烧伤了,相信我的伤痕很深。
有问题的使用可变。
class Bar(object):
def __init__(self,l = []):
self.l = l
def add(self,o):
self.l += [o]
def __repr__(self): return str(self.l)
尝试使用不可变的:
class Bar(object):
def __init__(self,l = None):
if l is None:
self.l = []
else:
self.l = l
def add(self,o):
self.l += [o]
def __repr__(self): return str(self.l)
其他人已经解释了这个问题,并建议使用l=None
和一个明确的测试。 我想提出一个不同的方法:
class Bar(object):
def __init__(self, l=[]):
self.l = list(l)
# and so on...
这默认每次都保证一个新的空白列表,并且还确保如果调用者传入他们自己的列表,您将获得该列表的副本,而不是对调用者列表的引用。 作为一个额外的好处,调用者可以传入列表构造函数可以使用的任何内容,例如元组或字符串,并且您的代码不必担心这一点; 它只能处理一个列表。
如果您只是保存对调用者给出的列表的引用,并稍后更改名为self.l
的列表,则可能无意中更改了传入的列表(因为您的类和调用者现在都引用了同一个对象)。 同样,如果他们在调用构造函数后更改了列表,那么您的“副本”也将被更改(因为它实际上不是副本)。 当然,这可能是你想要的行为,但可能不是这种情况。
虽然如果你从不操纵列表(即添加,替换或删除项目),但只是引用它或批量替换它,复制它通常是浪费时间和内存。
使用list()
构造函数创建的副本很浅。 也就是说,列表本身是一个新对象,但如果原始列表包含对可变对象的引用(例如其他列表或字典,或大多数其他类的实例),则更改这些对象时会出现同样的问题,因为它们仍然在列表之间共享。 这个问题的频率低于您的想象,但如果是这样,您可以使用copy
模块中的deepcopy()
函数执行深层复制。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.