¿Cómo / por qué funciona set () en {frozenset ()}?

A pesar de que los conjuntos son inestables, la verificación de membresía en otros trabajos de conjuntos:

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

Esperaba TypeError: unhashable type: 'set' , consistente con otros comportamientos en 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' 

Entonces, ¿cómo se implementa la pertenencia a otro conjunto?

La última línea de la documentación para el set s trata esto:

Tenga en cuenta que el argumento elem de los __contains__() , remove() y discard() puede ser un set . Para apoyar la búsqueda de un frozenset equivalente, se crea uno temporal desde elem .

set_contains se implementa de esta manera :

 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; } 

Por lo tanto, esto delegará directamente a set_contains_key lo que esencialmente hará un hash del objeto y luego buscará el elemento usando su hash.

Si el objeto es inestable, set_contains_key devuelve -1 , así que nos set_contains_key dentro de eso if . Aquí, verificamos explícitamente si el objeto key pasado es un conjunto (o una instancia de un subtipo de conjunto) y si previamente obtuvimos un error de tipo. Esto sugeriría que intentamos una verificación de contención con un set pero que falló porque es inestable.

En esa situación exacta, ahora creamos un nuevo frozenset partir de ese set e intentamos la verificación de la contención usando set_contains_key nuevamente. Y como los frozensets son hashables correctamente, podemos encontrar nuestro resultado de esa manera.

Esto explica por qué los siguientes ejemplos funcionarán correctamente a pesar de que el conjunto en sí no es hashable:

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