以下是相关代码的简化版本:
class thing:
def __init__(self, data1, data2={'foo': 1}):
self.data2 = data2
self.data2['bar'] = data1
datas = ['FIRST', 'SECOND']
things = [thing(x) for x in datas]
for p in things:
print p.data2['bar']
我希望期望此代码返回:
FIRST
SECOND
然而,实际上会返回:
SECOND
SECOND
为什么?
我最好的猜测是:
ur_dict = {'foo': 1}
,thing
的对象时,我不创建新词典self.data2={'foo': 1}
,而是初始化对ur_dict
的引用,bar: data1
添加到self.data2
时,我实际上是将该键和值添加到ur_dict
本身。这将更新列表中每个对象的data2
字段。我通过将以下代码段附加到上面的代码来测试这个假设:
things[0].data2['bar'] = 'FIRST'
for p in things:
print p.data2['bar']
果然,结果是:
FIRST
FIRST
所以我认为我的假设可能是正确的,但对我来说仍然是个谜。我的问题似乎与this问题非常相关---当我在构造函数中创建默认值时,这个值是 class 变量而不是实例变量吗?为什么python不为对象中的参数的默认值创建新对象?是否有一种很好的方法来概念化或在将来发现这种错误?有什么好的参考资料来了解这里发生了什么?
(如果我用行话来修改术语,请提前道歉;我是正规教育的数学家。)
编辑:@CrazyPython[::-1]
提到这是函数的属性,而不是类或对象。我创建了一个带有可变默认参数的函数示例,我试图弄清楚如何以我在上面的对象和类中遇到的方式来打破它。想出了这个:
EXPONENT = 1
def fooBar(x, y=EXPONENT):
return x**y
print EXPONENT
print foo(5)
EXPONENT = 2
print EXPONENT
print foo(5)
然而,它返回:
1
5
2
5
什么是“打破”这个以解释为什么不在这里使用可变默认参数的好方法?
答案 0 :(得分:2)
self.data2={'foo': 1}
是一个可变的默认参数。永远不要那样做。 99%的时间都是错误的。
def foo(a, b={}):
...
不等于
def foo(a, b=None):
if b is None:
b = {}
每次都不会重建{p> b
。这是同一个目标。
将来是否有一种很好的方法来概念化或捕捉这种错误?
使用PyCharm或其他好的编辑器
当我在构造函数中创建默认值时,此值是一个类变量而不是实例变量吗?
不不不不不不。它与函数有关,而不是类或实例。这种行为适用于所有功能。它与课程或您的链接问题无关。
为什么python不为对象中的参数默认值创建新对象?
性能和兼容性。构造对象是一种可能很昂贵的操作。
您不是继承object
。 That's bad.*
为什么这个python代码会改变列表中每个对象的字段?
没有上下文,这是一个糟糕的标题。它询问代码。当我们阅读标题时,我们没有代码。
声明函数时,将计算默认参数。 (不是在执行时,当它被陈述时)它们与功能相关联。执行函数时,不会生成默认参数的副本。您可以直接修改默认参数。调用的下一个函数将接收相同的对象,并进行修改。
<子> *你似乎是一名教授或类似的人。请不要被模因冒犯。这只是从互联网上获得建议的风险。 子>