首页 > 解决方案 > 使对象相对于其父级的父级不可变

问题描述

背景

如果用户定义的对象具有不可变对象作为属性,则在该属性上使用+=运算符与在指向该属性的另一个变量上使用它具有不同的效果:

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

现在xa.x指向同一个对象:id(x) == id(a.x) and x == a.x == 6

x += 1

现在xa.x指向不同的对象,id(x) != id(a.x) and x == 7 and a.x == 6

我的问题

是否可以实现一个B具有属性的a类,使得+=操作符以相同的方式工作b.a.x?即我想要以下行为

要求 1

b = B()

现在我想b.a.x == 5

要求 2

b.a.x += 1

现在我想b.a.x == 6

要求 3

a = b.a
a.x += 1

现在我想b.a.x == 6。我不想增加a.x影响b.a.x

要求 4

a = A()
b.a = a

现在我想b.a.x == 5

要求 5

x = b.a.x
a = b.a

现在我想x == 5 and a.x == 5 and b.a.x == 5

要求 6

x += 1

现在我想x == 6 and a.x == 5 and b.a.x == 5。我不想增加x影响a.xor b.a.x

换句话说,我希望能够使用+=操作符来影响b.a.x,但只有当它直接应用于b.a.x时,而不是当它应用于操作时绑定到同一对象的另一个名称时。

我试过的

B类的简单定义不起作用:

class B():
    def __init__:
        self.a = A()

这不符合要求 3。

但是,如果我更改b.a为返回新副本的属性a,那么这也不起作用:

class B()
    def __init__:
        self._a = A()

    @property
    def a(self):
        return copy(_a)

    @a.setter
    def a(self, value)
        self._a = value

这不符合要求 2。

我还尝试使用返回新类实例__iadd__方法将 X 实现为新类。这意味着x它在应用时仍然表现得好像它是不可变的+=,但我无法弄清楚如何同时实现上述要求 2 和 3。

我正在使用 Python 3。

标签: pythonimmutability

解决方案


所需的行为完全取决于如何+=.x

例如,如果x指向一个列表对象,则不会获得所需的行为,如果x指向一个int对象,则会获得该行为。

A因此,对于您的班级或班级,您实际上没有任何事情可以或需要做任何事情B来获得这种行为。

无论你做什么来获得这种行为,都必须以x


推荐阅读