As discussed on discuss.python.org.
Problem
The membership test equivalence in §6.10.2 has two issues:
1. Ignores hashing for sets/dicts. It says x in y for sets is equivalent to any(x is e or x == e for e in y) — a linear scan. Sets and dicts use hash lookup, so this equivalence is false when the hash invariant is violated (a == b but hash(a) != hash(b)). This was previously reported as bpo-45832 / #89990.
2. Observable identity check. The x is e short-circuit means membership can succeed via identity when equality fails (e.g., NaN). But the spec says nothing about whether repeated evaluations of non-literal expressions produce the same object, so this behavior is implementation-dependent.
Concrete example
Nuitka/Nuitka#3889 — Nuitka's constant blob cached float('nan') as a singleton, making nan2 in {nan1} return True instead of False. Both behaviors are spec-compliant because the spec doesn't address call identity. This is what motivated the fix.
Prior art / related issues
Proposed fix
Branch: clarify-membership-docs on KRRT7/cpython.
- Split the equivalence in §6.10.2: sequence types (linear scan) vs hash-based containers (hash lookup)
- Add a note acknowledging edge cases (NaN, mutable
__eq__)
- Add a clarifying sentence to §6.2.3.1 that non-literals make no identity guarantees
As discussed on discuss.python.org.
Problem
The membership test equivalence in §6.10.2 has two issues:
1. Ignores hashing for sets/dicts. It says
x in yfor sets is equivalent toany(x is e or x == e for e in y)— a linear scan. Sets and dicts use hash lookup, so this equivalence is false when the hash invariant is violated (a == b but hash(a) != hash(b)). This was previously reported as bpo-45832 / #89990.2. Observable identity check. The
x is eshort-circuit means membership can succeed via identity when equality fails (e.g., NaN). But the spec says nothing about whether repeated evaluations of non-literal expressions produce the same object, so this behavior is implementation-dependent.Concrete example
Nuitka/Nuitka#3889 — Nuitka's constant blob cached
float('nan')as a singleton, makingnan2 in {nan1}returnTrueinstead ofFalse. Both behaviors are spec-compliant because the spec doesn't address call identity. This is what motivated the fix.Prior art / related issues
ischeckProposed fix
Branch:
clarify-membership-docson KRRT7/cpython.__eq__)