9

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

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

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

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

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

谢谢!

4

4 回答 4

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'
于 2011-11-26T00:35:24.150 回答
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:没有简单、优雅、通用的方法来做到这一点。

于 2011-11-26T00:24:13.957 回答
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,那么这个检查将不起作用。

于 2011-11-26T00:14:50.557 回答
1

进行这样的测试绝对有意义!

考虑“deepcopy()-ing”(或手动 clone()-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
于 2013-11-11T07:18:30.700 回答