如何在Python中测试“任何深度的不变性”?

时间:2011-11-26 00:02:29

标签: python nested immutability hashable

我将Python对象定义为“在任何深度都是不可变的”iff

  1. 它(名义上)是不可变的;
  2. 如果它是一个“容器”对象,那么它只包含“在任何深度都是不可变的”对象;
  3. 例如((1, 2), (3, 4))在任何深度都是不可变的,而((1, 2), [3, 4])则不是(尽管后者由于是一个元组,“名义上”是不可变的)。

      

    有没有合理的方法来测试Python对象是否“在任何深度都是不可变的”?

    测试第一个条件相对容易(例如使用collections.Hashable类,忽略了不正确实现__hash__方法的可能性),但第二个条件更难测试,因为“容器”对象的异质性,以及迭代“内容”的方法...

    谢谢!

4 个答案:

答案 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