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