set_contains
is implemented 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;
}
So this will delegate directly to set_contains_key
which will essentially hash the object and then look up the element using its hash.
If the object is unhashable, set_contains_key
returns -1
, so we get inside that if
. Here, we check explicitly whether the passed key
object is a set (or an instance of a set subtype) and whether we previously got a type error. This would suggest that we tried a containment check with a set
but that failed because it is unhashable.
In that exact situation, we now create a new frozenset
from that set
and attempt the containment check using set_contains_key
again. And since frozensets are properly hashable, we are able to find our result that way.
This explains why the following examples will work properly even though the set itself is not hashable:
>>> set() in {frozenset()}
True
>>> set(('a')) in { frozenset(('a')) }
True
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…