如何在 Python 中测试"immutability-at-any-depth"?



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

  1. 它(名义上)是不可变的;
  2. 如果它是一个"容器"对象,那么它只包含"在任何深度都不可变"的对象;

例如,((1, 2), (3, 4))在任何深度都是不可变的,而((1, 2), [3, 4])则不是(尽管后者由于是元组,"名义上"是不可变的)。

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

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

谢谢!

不存在一般的不变性测试。只有当对象的所有方法都不能改变底层数据时,对象才是不可变的。

更有可能的是,你对哈希性感兴趣,而哈希性通常依赖于不变性。可哈希的容器将递归哈希其内容(即元组和冷冻集)。所以,你的测试相当于运行hash(obj),如果它成功了,那么它是深度哈希的。

现在,你的代码已经使用了最好的测试:

>>> 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'

我想你在寻找这样的东西:

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的观点:没有简单、优雅、通用的方法来做到这一点。

我不确定你到底在找什么。但是使用您的示例数据:

>>> 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的子类,那么此检查将不起作用。

有这样一个测试绝对是有意义的!

考虑'deepcopy()-ing'(或手动克隆()-ing)的时间对象vs简单引用赋值!

假设两个实体需要拥有同一个对象,但是依赖于它没有被改变(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

相关内容

最新更新