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

9

如果一个Python对象“在任何深度上都是不可变的”,则满足以下条件:

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

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

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

相对容易测试第一个条件(例如使用collections.Hashable类,并忽略未经适当实现的__hash__方法的可能性),但是第二个条件更难测试,因为“容器”对象的异构性以及迭代其“内容”的方法......

谢谢!


4
我认为这样的检查通常既不可能实现,也没有太大用处。你有任何使用案例吗? - user395760
为什么可哈希性意味着不可变性? - Gabe
https://dev59.com/lHE85IYBdhLWcg3wqVX5 - joaquin
更重要的是,可哈希对象理想情况下应该是不可变的,但只有内置类型实际上保证了这一点。 - Gabe
4个回答

5

没有通用的不可变性测试。只有当一个对象的所有方法都不能改变其基础数据时,它才是不可变的。

更可能的是,您对哈希性感兴趣,这通常取决于不可变性。可哈希的容器将递归哈希其内容(例如元组和frozenset)。因此,您的测试就是运行 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'

3
我想您可能正在寻找这样的内容:

我想您可能正在寻找这样的内容:

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 的观点:没有简单、优雅、通用的方法来解决这个问题。


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等子类实现不正确,则此检查将无法工作。


1

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

考虑一下“deepcopy()”(或手动克隆())对象所需的时间与简单引用赋值之间的时间差异!

想象两个实体需要拥有同一个对象,但依赖于它不被更改(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

1
我完全同意需要进行不可变性测试。我也喜欢你的递归测试。但是它只适用于有限数量的内置类型。我在思考是否可以将其扩展到用户定义的类上...即测试所有方法是否都不会改变对象。但如何进行测试呢?有什么想法吗? - HiFile.app - best file manager

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接