How/why {frozenset()} 中的 set() 有效吗?

How/why does set() in {frozenset()} work?

即使集不可散列,其他集的成员资格检查也有效:

>>> set() in {frozenset()}
True

我预计 TypeError: unhashable type: 'set',与 Python 中的其他行为一致:

>>> set() in {}  # doesn't work when checking in dict
TypeError: unhashable type: 'set'
>>> {} in {frozenset()}  # looking up some other unhashable type doesn't work
TypeError: unhashable type: 'dict'

那么,其他集合中的集合成员是如何实现的?

the documentation for sets 的最后一行讨论了这个:

Note, the elem argument to the __contains__(), remove(), and discard() methods may be a set. To support searching for an equivalent frozenset, a temporary one is created from elem.

set_containsimplemented like this:

static int
set_contains(PySetObject *so, PyObject *key)
{
    PyObject *tmpkey;
    int rv;

    rv = set_contains_key(so, key);
    if (rv < 0) {
        if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError))
            return -1;
        PyErr_Clear();
        tmpkey = make_new_set(&PyFrozenSet_Type, key);
        if (tmpkey == NULL)
            return -1;
        rv = set_contains_key(so, tmpkey);
        Py_DECREF(tmpkey);
    }
    return rv;
}

所以这将直接委托给 set_contains_key,后者将对对象进行哈希处理,然后使用其哈希查找元素。

如果对象是不可散列的,set_contains_key returns -1,所以我们进入那个 if。在这里,我们检查显式传递的key对象是否是集合(或集合子类型的实例)我们之前是否出现类型错误。这表明我们尝试使用 set 进行包含检查,但失败了,因为它不可散列。

在那种情况下,我们现在从 set 创建一个新的 frozenset 并再次尝试使用 set_contains_key 进行收容检查。由于 frozensets 是可适当散列的,因此我们能够以这种方式找到我们的结果。

这解释了为什么即使集合本身不可哈希,以下示例也能正常工作:

>>> set() in {frozenset()}
True
>>> set(('a')) in { frozenset(('a')) }
True