Python 使对象相对于其父对象不可变';父母 背景

Python 使对象相对于其父对象不可变';父母 背景,python,immutability,Python,Immutability,如果用户定义的对象具有一个不可变对象作为属性,则在该属性上使用+=操作符与在指向该属性的另一个变量上使用它具有不同的效果: 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

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

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)和x==a.x==6

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

x += 1
我的问题 是否可以实现具有属性
a
的类
B
,以便
+=
操作符以相同的方式在
B.a.x
上工作?i、 e.我想要以下行为

要求1 现在我想要
b.a.x==5

要求2 现在我想要
b.a.x==6

x = a.x
要求3 现在我想要
b.a.x==6
。我不希望增量
a.x
影响
b.a.x

要求4 现在我想要
b.a.x==5

要求5 现在我想要
x==5和a.x==5和b.a.x==5

要求6 现在我想要
x==6和a.x==5和b.a.x==5
。我不希望递增
x
影响
a.x
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。

我还尝试将X实现为一个返回新类实例的类。这意味着当应用
+=
时,
x
仍然像是不可变的,但我不知道如何实现上面的要求2和3


我在Python 3中工作。

TL;博士,你所问的是不可能的,而且违背了语言本身的许多承诺

考虑要求3
您创建了一个新的B
B()
(它在其构造函数中创建了a的实例)实例,并且一个引用
B
指向此实例

b.a
是指向已创建的实例的另一个引用。
此代码
x=b.a
仅创建指向a的同一实例的引用。对对象所做的任何更改都将通过指向它的两个引用可见。

这是有意义的,因为A的这个实例不知道(也不关心)它是否是另一个实例的成员。在这两种情况下,其行为应该是相同的。(这是python语言的承诺,对象的行为与引用对象的行为相同)。

所需的行为完全取决于
+=
操作符如何为类型的
x
实现

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

因此,对于类
A
或类
B
来说,要获得这种行为,实际上没有什么可以或需要做的


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

类型中完成。我想我已经在我的描述中介绍了
int
的不变性。如果我写的东西有错误,你能进一步解释一下吗?我认为你的建议解决不了问题。如果客户机写入
a=b.a
,那么现在
a
b.a
被绑定到同一个对象,随后
a.x+=1
将影响这两个对象。行
b=b()
a=b.a
在客户机代码中:要求客户写入
a=b.copyOfA()
而不是
a=b.a
将不幸地破坏此API设计的目的。任何解决方案都必须存在于我的API代码中定义的类B、A(可能还有X)的定义中。谢谢您的回答。你能给我一些关于如何实现一个
x.\uuuu iadd\uuuu
来实现上述目标的提示吗?…因为使用
int
并不能给我想要的结果behaviour@Rich. 我不确定我能帮上什么忙,但在评论部分的讨论可能是相关的,甚至是有用的,我想谢谢:我已经看到了这个问题,但它无助于解决这个具体问题。我将为我的问题添加一个链接,以便更清楚地说明我已经知道了这一点。@Rich:由于您的要求3,您对
X.\uuu iadd\uu()
的拦截应该确保
b.a.X
a.X
最终指向两个不同的
X
对象。但这只能通过使
b.a
a
指向两个不同的
a
对象来实现,即使最初(在调用
X.\uu iadd\uuu()
之前)它们指向相同的
a
对象。这似乎是一件不可能做到的事情——但由于我对Python只有2个月的了解,我还不能排除这种可能性。我将在前面指向的链接的注释中再次查看
资源
类示例。您确定需求3中提到的观察到的行为吗?你是否真的观察到了这种行为,或者你只是说这就是你想要它的行为?我这样问是因为,你在那里使用的语言表明你确实观察到了这种行为。那种行为与我自己的行为完全背道而驰
x = b.a.x
a = b.a
x += 1
class B():
    def __init__:
        self.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