我在New-Style Classes中发现了子类化和字典更新的一个奇怪问题:
Python 2.6.2 (r262:71605, Apr 14 2009, 22:40:02) [MSC v.1500 32 bit (Intel)] on
win32
>>> class a(object):
... def __init__(self, props={}):
... self.props = props
...
>>> class b(a):
... def __init__(self, val = None):
... super(b, self).__init__()
... self.props.update({'arg': val})
...
>>> class c(b):
... def __init__(self, val):
... super(c, self).__init__(val)
...
>>> b_inst = b(2)
>>> b_inst.props
{'arg': 2}
>>> c_inst = c(3)
>>> c_inst.props
{'arg': 3}
>>> b_inst.props
{'arg': 3}
>>>
在调试中,在第二次调用(c(3)
)中,您可以看到a
内的构造函数self.props
已经等于{'arg': 2}
,并且b
构造函数在此之后被调用,对于两个对象都变为{'arg': 3}
!
另外,构造函数调用的顺序是:
a, b # for b(2)
c, a, b # for c(3)
如果您在self.props.update()
构造函数中使用self.props = {'arg': val}
更改b
,一切都会正常,并会按预期执行
但我真的需要更新这个属性,而不是替换
答案 0 :(得分:11)
props
不应该有这样的默认值。这样做:
class a(object):
def __init__(self, props=None):
if props is None:
props = {}
self.props = props
这是一个常见的python "gotcha"。
答案 1 :(得分:7)
你的问题在这一行:
def __init__(self, props={}):
{}是一种可变类型。并且在python默认参数值中只评估一次。这意味着您的所有实例都在共享相同的字典对象!
要解决此问题,请将其更改为:
class a(object):
def __init__(self, props=None):
if is None:
props = {}
self.props = props
答案 2 :(得分:2)
简短版本:执行此操作:
class a(object):
def __init__(self, props=None):
self.props = props if props is not None else {}
class b(a):
def __init__(self, val = None):
super(b, self).__init__()
self.props.update({'arg': val})
class c(b):
def __init__(self, val):
super(c, self).__init__(val)
长版:
函数定义只计算一次,因此每次调用它时都会使用相同的默认参数。为了使其按预期工作,每次调用函数时都必须评估默认参数。但是Python会生成一次函数对象,并将默认值添加到对象(如func_obj.func_defaults
)