为什么构造一个新对象可能会改变该 class 的 *每个* 实例的字段?

Why does constructing a new object potentially alter the field of *every* instance of that class?

这是相关代码的简化版本:

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']

希望此代码return:

FIRST
SECOND

然而,实际上 returns:

SECOND
SECOND

为什么?


我的最佳猜测是:

我通过将以下代码片段附加到上面的代码来测试这个假设:

things[0].data2['bar'] = 'FIRST'
for p in things:
    print p.data2['bar']

果然结果是:

FIRST
FIRST

所以我认为我的假设可能是正确的,但它对我来说仍然很神秘。我的问题似乎与 问题非常相关---当我在构造函数中创建默认值时,这个值是 class 变量而不是实例变量?为什么 python 不为对象中参数的默认值创建新对象?有没有一种好的方法来概念化或在将来捕获这种错误?了解这里发生的事情有哪些好的参考资料?

(如果我弄错行话,请提前致歉;我是一名受过正规教育的数学家。)


编辑:@CrazyPython[::-1] 提到这是一个 属性 函数,而不是 class 对象。我创建了一个带有可变默认参数的函数示例,我试图弄清楚如何以我在上述对象和 classes 中遇到的方式打破它。想出了这个:

EXPONENT = 1
def fooBar(x, y=EXPONENT):
    return x**y
print EXPONENT
print foo(5)
EXPONENT = 2
print EXPONENT
print foo(5)

但是,returns:

1
5
2
5

什么是 "break" 说明为什么不在这里使用可变默认参数的好方法?

self.data2={'foo': 1} 是一个可变的默认参数。永远不要那样做。 99% 的时候这是一个错误。

def foo(a, b={}):
    ...

不等于

def foo(a, b=None):
    if b is None:
        b = {}

b不是每次都重构。一样object.

Is there a good way to conceptualize or to catch this kind of error in the future?

使用PyCharm或其他好的编辑器

when I create a default value in a constructor, is this value a class variable instead of an instance variable?

不不不不不不。它与函数相关,而不是 class 或实例。这是适用于所有函数的行为。它与 classes 或您的链接问题无关。

Why doesn't python create new objects for default values of arguments in an object?

性能和兼容性。构造一个 object 是一个潜在的昂贵操作。


附录

  • class请使用 CamelCase。不这样做是违反 PEP 8 的,这是 Python 官方风格指南。
  • 找一个像样的编辑器。个人推荐PyCharm。 (我不是销售人员)它是免费的。
    • PyCharm 可以重命名变量、突出显示 PEP 8 错误、执行高级自动完成、类型检查、发现可变默认参数等等。
  • 您没有继承 objectThat's bad.*

    Why does this python code alter the field of every object in a list?

  • 没有上下文,这是一个糟糕的标题。它询问代码。看标题的时候没有代码


让它更清晰

当声明一个函数时,会计算默认参数。 (不是在执行时,在声明时)它们与功能相关。执行函数时,不会生成默认参数的副本。您直接修改默认参数。下一个被调用的函数将收到相同的 object,但有修改。

*您似乎是教授之类的。请不要被模因冒犯。这只是从互联网上获得建议的风险。