2017-07-19 79 views
-1

考虑在Python 3.5.2以下代码:使用类型的字典作为属性在Python 3

class NewObj(): 

    def __init__(self, refs={}): 
     self.refs = refs 

class ObjA(NewObj): 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 

class ObjB(NewObj): 
    def __init__(self, *args, **kwargs): 
     super().__init__(*args, **kwargs) 
     self.refs['c'] = 3 

a = ObjA(refs={'a':1, 'b': 2}) 
b = ObjA() 
c = ObjB() 
lst = [a, b, c] 

for obj in lst: 
    print('%s has refs: %s' % (obj, obj.refs)) 

代码的输出是:

<__main__.ObjA object at 0x7f74f0f369b0> has refs: {'a': 1, 'b': 2} 
<__main__.ObjA object at 0x7f74f0f36a90> has refs: {'c': 3} 
<__main__.ObjB object at 0x7f74f0f36ac8> has refs: {'c': 3} 

它是输出的秒线这使我感到困惑 - 在我看来应该输出一个空字典。原因是因为b被分配了一个ObjA的实例而没有调用任何参数,所以根据默认初始化,b.refs == {}应该是True

这是一个错误或期望的行为?如果它不是一个bug,能否请我解释为什么这是所需的行为,以及为获得我想要的输出的代码的最小改变(即当没有提供参数时,.refs被初始化为空dict)?

+0

这是一个众所周知的情况下新的Python程序员就可以得到意外。当定义函数时,使用'refs = {}'声明的默认参数只创建一次。所有使用默认值的调用都会获得对同一个字典的引用(而不是每个字典的新空字典)。 – Blckknght

回答

1

由于@blckknght正确指出,这实际上是故意行为和重复。然而,问题的次要部分 - 关于最小的必要变更 - 在副本中不容易找到。根据文档:

当执行函数定义时,从左到右评估默认参数值。这意味着该表达式在函数被定义时被评估一次,并且每次调用都使用相同的“预先计算”值。这对于了解默认参数是否为可变对象(如列表或字典)时尤为重要:如果函数修改对象(例如,通过将项目附加到列表中),则实际上会修改默认值。这通常不是预期的。解决的办法是使用无作为默认,并在函数体明确地测试它,例如:

def whats_on_the_telly(penguin=None): 
    if penguin is None: 
     penguin = [] 
    penguin.append("property of the zoo") 
    return penguin