python – 保证哪些对象具有不同的身份?
作者:互联网
原始问题:
(我的问题适用于Python 3.2,但我怀疑自Python 2.7以来这已经发生了变化.)
假设我使用我们通常期望创建对象的表达式.例子:[1,2,3]; 42; ‘ABC’;范围(10);真正;开放( ‘README.TXT’);我的课(); lambda x:2 * x;等等
假设在不同的时间执行两个这样的表达式并“评估为相同的值”(即,具有相同的类型,并且相等地进行比较).在什么条件下Python提供我所谓的独特对象保证两个表达式实际上创建两个不同的对象(即,x是y计算为False,假设两个对象绑定到x和y,并且两者都在同时)?
我理解对于任何可变类型的对象,“不同对象保证”包含:
x = [1,2]
y = [1,2]
assert x is not y # guaranteed to pass
我也知道某些不可变类型(str,int)保证不成立;对于某些其他不可变类型(bool,NoneType),相反的保证成立:
x = True
y = not not x
assert x is not y # guaranteed to fail
x = 2
y = 3 - 1
assert x is not y # implementation-dependent; likely to fail in CPython
x = 1234567890
y = x + 1 - 1
assert x is not y # implementation-dependent; likely to pass in CPython
但是所有其他不可变类型呢?
特别是,在不同时间创建的两个元组是否具有相同的标识?
我对此感兴趣的原因是我将图形中的节点表示为int的元组,并且域模型使得任何两个节点都是不同的(即使它们由具有相同值的元组表示).我需要创建一组节点.如果Python保证在不同时间创建的元组是不同的对象,我可以简单地将元组子类化为重新定义相等以表示标识:
class DistinctTuple(tuple):
__hash__ = tuple.__hash__
def __eq__(self, other):
return self is other
x = (1,2)
y = (1,2)
s = set(x,y)
assert len(s) == 1 # pass; but not what I want
x = DistinctTuple(x)
y = DistinctTuple(y)
s = set(x,y)
assert len(s) == 2 # pass; as desired
但是如果不能保证在不同时间创建的元组是不同的,那么上面是一种可怕的技术,它隐藏了一个可能随机出现并可能很难复制和发现的休眠错误.在这种情况下,子类化将无济于事;我实际上需要添加到每个元组,作为一个额外的元素,一个唯一的ID.或者,我可以将我的元组转换为列表.无论哪种方式,我都会使用更多内存.显然,除非我原来的子类化解决方案不安全,否则我宁愿不使用这些替代方案.
我的猜测是Python不为内置或用户定义的不可变类型提供“独特的对象保证”.但我没有在文档中找到关于它的明确声明.
更新1:
@LuperRouch @larsmans感谢您的讨论和答案到目前为止.这是我还不清楚的最后一个问题:
Is there any chance that the creation of an object of a user-defined
type results in a reuse of an existing object?
如果这是可能的,我想知道如何验证我使用的任何类是否可能表现出这种行为.
这是我的理解.每次创建用户定义的类的对象时,首先调用类’__new __()方法.如果重写此方法,则语言中的任何内容都不会阻止程序员返回对现有对象的引用,从而违反了我的“不同对象保证”.显然,我可以通过检查类定义来观察它.
我不确定如果用户定义的类没有覆盖__new __()(或者从基类显式依赖__new __())会发生什么.如果我写
class MyInt(int):
pass
对象创建由int .__ new __()处理.我希望这意味着我有时可能会看到以下断言失败:
x = MyInt(1)
y = MyInt(1)
assert x is not y # may fail, since int.__new__() might return the same object twice?
但在我对CPython的实验中,我无法实现这种行为.这是否意味着语言为不覆盖__new__的用户定义类提供了“独特的对象保证”,或者它只是一个任意的实现行为?
更新2:
虽然我的DistinctTuple被证明是一个非常安全的实现,但我现在明白,使用DistinctTuple来模拟节点的设计理念非常糟糕.
身份操作符已经使用该语言;使= =的行为方式与逻辑上是多余的.
更糟糕的是,如果==本可以做一些有用的事情,我就让它无法使用.例如,很可能在我的程序中的某个地方我想看看两个节点是否由同一对整数表示; ==本来是完美的 – 事实上,这就是它默认做的事情……
更糟糕的是,大多数人确实期望==比较一些“价值”而不是身份 – 即使是用户定义的类.他们不会意识到我的覆盖只会看到身份.
最后……我必须重新定义==的唯一原因是允许具有相同元组表示的多个节点成为集合的一部分.这是错误的方法!这不是需要改变的==行为,而是容器类型!我只需要使用多集而不是集合.
简而言之,虽然我的问题可能对其他情况有一些价值,但我绝对相信创建类DistinctTuple对我的用例来说是一个糟糕的想法(我强烈怀疑它根本没有有效的用例).
解决方法:
Is there any chance that the creation of an object of a user-defined type results in a reuse of an existing object?
当且仅当用户定义的类型明确设计为执行此操作时,才会发生这种情况.使用__new __()或某些元类.
I’d like to know how I can verify for any class I work with whether it might exhibit such a behavior.
使用来源,卢克.
当涉及到int时,预先分配小整数,并且在使用整数创建计算的任何地方使用这些预分配的整数.当你做MyInt(1)是MyInt(1)时,你不能让这个工作,因为你有的不是整数.然而:
>>> MyInt(1) + MyInt(1) is 2
True
这是因为MyInt(1)MyInt(1)当然没有返回MyInt.它返回一个int,因为这就是整数的__add__返回的内容(也就是检查预分配整数的位置).如果有的话只是表明通常对int进行子类化并不是特别有用.
标签:object-identity,python,python-3-x,object 来源: https://codeday.me/bug/20190726/1539198.html