我将Python对象定义为“在任何深度都是不可变的”iff
例如((1, 2), (3, 4))
在任何深度都是不可变的,而((1, 2), [3, 4])
则不是(尽管后者由于是一个元组,“名义上”是不可变的)。
有没有合理的方法来测试Python对象是否“在任何深度都是不可变的”?
测试第一个条件相对容易(例如使用collections.Hashable
类,忽略了不正确实现__hash__
方法的可能性),但第二个条件更难测试,因为“容器”对象的异质性,以及迭代“内容”的方法...
谢谢!
答案 0 :(得分:5)
对不变性没有一般性测试。只有当一个对象的方法都不能改变底层数据时,该对象才是不可变的。
更有可能的是,您对可靠性感兴趣,这通常取决于不变性。可清洗的容器将递归地散列其内容(即元组和后代)。因此,您的测试相当于运行hash(obj)
,如果成功,则可以深度清理。
IOW,你的代码已经使用了最好的测试:
>>> a = ((1, 2), (3, 4))
>>> b = ((1, 2), [3, 4])
>>> hash(a)
5879964472677921951
>>> hash(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
答案 1 :(得分:2)
我不确定你到底在寻找什么。但是使用您的示例数据:
>>> a = ((1, 2), (3, 4))
>>> b = ((1, 2), [3, 4])
>>> isinstance(a, collections.Hashable)
True
>>> isinstance(b, collections.Hashable)
True
因此,确实使用collections.Hashable
不是可行的方法。然而,
>>> hash(a)
5879964472677921951
>>> hash(b)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
因此,至少对于示例数据,使用hash
足以验证对象是否可以清除。当然,正如您在问题中已经指出的那样,如果__hash__
未正确实现了list
的子类,那么此检查将不起作用。
答案 2 :(得分:2)
我想你正在寻找这样的东西:
def deeply_hashable(obj):
try:
hash(obj)
except TypeError:
return False
try:
iter(obj)
except TypeError:
return True
return all(deeply_hashable(o) for o in obj)
这里一个明显的问题是迭代dict
遍历其键,这些键总是不可变的,而不是它的值,这是你感兴趣的。除此之外没有简单的方法,除此之外当然来自特殊套管dict
- 这对其他可能表现相似但不是来自dict
的类没有帮助最后,我同意delnan:没有简单,优雅,一般的方法来做到这一点。
答案 3 :(得分:1)
进行这样的测试绝对有意义!
考虑'deepcopy() - ing'(或手动克隆() - ing)的时间 一个对象与一个简单的引用分配!
想象一下,两个实体需要拥有一个相同的对象,但是 依赖于它没有改变(dict-keys是一个很好的例子)。
然后,只有当使用引用赋值时才是安全的 如果可以验证不变性。
我会考虑递归测试像
这样的东西def check(Candidate):
if isinstance(Candidate, (str, int, long)):
return True
elif isinstance(Candidate, tuple):
return not any(not check(x) for x in Candidate)
else:
return False