如果用户定义的对象具有不可变的对象作为属性,则在属性上使用+=
运算符与在指向该属性的另一个变量上使用它具有不同的效果:
class A():
def __init__(self):
# Set self.x to be an (immutable) int
self.x = 5
a = A()
现在a.x == 5
a.x += 1
现在a.x
指向另一个对象,并且a.x == 6
指向
x = a.x
现在x
和a.x
指向同一对象:id(x) == id(a.x) and x == a.x == 6
x += 1
现在x
和a.x
指向不同的对象id(x) != id(a.x) and x == 7 and a.x == 6
是否可以实现具有属性B
的类a
,以使+=
运算符在b.a.x
上以相同的方式工作?即 我想要以下行为 :
b = B()
现在我要b.a.x == 5
。
b.a.x += 1
现在我要b.a.x == 6
。
a = b.a
a.x += 1
现在,我想要b.a.x == 6
。我不希望增加a.x
以影响b.a.x
。
a = A()
b.a = a
现在我要b.a.x == 5
。
x = b.a.x
a = b.a
现在我要x == 5 and a.x == 5 and b.a.x == 5
。
x += 1
现在,我想要x == 6 and a.x == 5 and b.a.x == 5
。我不想增加x
来影响a.x
或b.a.x
。
换句话说,我希望能够使用+=
运算符来影响b.a.x
,但是将 only 直接应用于{{1} },而不是在操作时将其应用于绑定到同一对象的另一个名称时。
B类的简单定义不起作用:
b.a.x
此要求不满足3。
但是,如果我将class B():
def __init__:
self.a = A()
更改为一个返回b.a
的新副本的属性,则这也不起作用:
a
此要求未通过2。
我还尝试将X实现为返回新类实例的new class with an __iadd__
method。这意味着class B()
def __init__:
self._a = A()
@property
def a(self):
return copy(_a)
@a.setter
def a(self, value)
self._a = value
在应用x
时仍然表现为不变,但是我无法弄清楚如何同时满足上述2和3要求。
我正在使用Python 3。
答案 0 :(得分:1)
期望的行为完全取决于+=
类型的x
运算符的实现方式。
例如,如果x
指向列表对象,则不会得到所需的行为,如果x
指向int
对象,则会得到该行为。 / p>
因此,您的类A
或类B
确实无法执行任何操作或需要执行任何操作来获得这种行为。
无论您采取什么措施来获得此行为,都必须以x
答案 1 :(得分:0)
TL; DR您所要提出的要求是不可能的,并且违反了语言本身的许多承诺。
考虑要求3
您创建了一个B的新实例B()
(它在其构造函数中创建了A的实例),引用b
指向该实例。
b.a
是另一个参考,指向已经创建的A实例。
这段代码x = b.a
只会创建一个指向A的相同实例的引用。对该对象所做的任何更改都将通过指向该对象的两个引用可见。
这是有道理的,因为这个A实例不知道(也不在乎)它是否是另一个实例的成员。在两种情况下,其行为都应相同。 (这是python语言的承诺,无论从何处引用该对象,其行为都相同。)