为什么构造一个新对象可能会改变该 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
为什么?
我的最佳猜测是:
- 我正在创建一个字典
ur_dict = {'foo': 1}
,
- 当我初始化 class
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
所以我认为我的假设可能是正确的,但它对我来说仍然很神秘。我的问题似乎与 问题非常相关---当我在构造函数中创建默认值时,这个值是 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 错误、执行高级自动完成、类型检查、发现可变默认参数等等。
您没有继承 object
。 That's bad.*
Why does this python code alter the field of every object in a list?
没有上下文,这是一个糟糕的标题。它询问代码。看标题的时候没有代码
让它更清晰
当声明一个函数时,会计算默认参数。 (不是在执行时,在声明时)它们与功能相关。执行函数时,不会生成默认参数的副本。您直接修改默认参数。下一个被调用的函数将收到相同的 object,但有修改。
*您似乎是教授之类的。请不要被模因冒犯。这只是从互联网上获得建议的风险。
这是相关代码的简化版本:
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
为什么?
我的最佳猜测是:
- 我正在创建一个字典
ur_dict = {'foo': 1}
, - 当我初始化 class
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
所以我认为我的假设可能是正确的,但它对我来说仍然很神秘。我的问题似乎与
(如果我弄错行话,请提前致歉;我是一名受过正规教育的数学家。)
编辑:@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 错误、执行高级自动完成、类型检查、发现可变默认参数等等。
您没有继承
object
。 That's bad.*Why does this python code alter the field of every object in a list?
没有上下文,这是一个糟糕的标题。它询问代码。看标题的时候没有代码
让它更清晰
当声明一个函数时,会计算默认参数。 (不是在执行时,在声明时)它们与功能相关。执行函数时,不会生成默认参数的副本。您直接修改默认参数。下一个被调用的函数将收到相同的 object,但有修改。
*您似乎是教授之类的。请不要被模因冒犯。这只是从互联网上获得建议的风险。