原来的问题:
(我的问题适用于Python的3.2以上版本,但我怀疑这是由于Python 2.7这种情况已经改变。)
假设我用的是我们通常希望创建一个对象的表达式。 实施例: [1,2,3]
; 42
; 'abc'
; range(10)
; True
; open('readme.txt')
; MyClass()
; lambda x : 2 * x
; 等等
假设两个这样的表达式在不同的时间执行,并且“评价为相同的值的”(即,具有相同的类型,并且比较结果为相等)。 在什么情况下的Python提供我所说的不同的对象保证两个表达式实际创建两个不同的对象(即x is 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的保证是不同的对象,我可以简单地子tuple
重新确定的平等是指身份:
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谢谢你的讨论,到目前为止答案。 这里的最后一个问题,我目前还不清楚有:
是否有任何机会,在现有对象的再利用创建的用户定义类型的结果对象的?
如果这是可能的,我想知道我可以验证我是否可能会表现出这样的行为,任何工作类。
这是我的理解。 创建用户定义的类的对象的任何时间,类__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
模型的节点是非常糟糕的。
身份操作是在语言中已经提供; 制作==
作为同样的行为方式is
在逻辑上是多余的。
更糟的是,如果==
本来可以做一些有用的东西,我把它不可用。 例如,它很可能是某个地方在我的计划,我会想看看如果两个节点通过同一对整数来表示; ==
本来非常适合-而事实上,这就是它的默认操作...
更糟糕的是,大多数人其实希望==
比较一些“价值”,而不是身份-即使是一个用户定义的类。 他们会跟我重写只看身份被措手不及。
最后......我不得不重新定义的唯一原因==
是允许使用相同的元组表示多个节点是一组的一部分。 这是错误的方式去了解它! 这不是==
需要改变自己的行为,它的容器类型! 我只是需要使用多集,而不是套。
总之,虽然我的问题可能有其他情况有些价值,我绝对相信,创造class DistinctTuple
是我的使用情况非常糟糕的主意(我强烈怀疑它有没有有效的用例的话)。