首页 > 解决方案 > Why does set() in set() not raise a TypeError, unlike {} in set(), [] in set(), set() in {}, [] in {}, or {} in {}?

问题描述

Sets and dicts are unhashable thus can't contain sets, dicts, or lists as keys. Even checking for containment raises a TypeError, as in all these five cases:

but checking if a set contains a set does not raise a TypeError:

returns False.

Why is set() in set() handled differently from {} in {}, [] in {}, set() in {}, {} in set(), or [] in set()?

标签: pythonset

解决方案


Good question! It doesn't appear to be documented, but staring at the implementation code it does appear to be intentional. While sets can't contain sets, they can contain frozensets. For example,

>>> s = {frozenset({2, 3}), frozenset({5, 7})}
>>> s
{frozenset({2, 3}), frozenset({5, 7})}
>>> type(s)
<class 'set'>

So s contains frozensets, but is not itself a frozenset. Now in this:

>>> {7, 5} in s
True

the implementation of set.__contains__() first tries to see whether {7, 5} is in s. That raises a TypeError ("key not hashable") internally. If checking for a key raises a type error, and the key is of a set type, then the implementation clears that error, builds a temporary frozenset from the original key, and tries again.

Offhand, I would not have done that (too magical), but perhaps there was some compelling use case I'm unaware of.

Correction

It is documented, but as the very last line of the set docs:

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.


推荐阅读