From 0ecf8ecc86716c745b8af7fb079f5f0f2cc2a011 Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Wed, 20 May 2026 12:13:20 -0500 Subject: [PATCH] gh-150165: Clarify membership test semantics for hash-based containers and object identity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the membership test equivalence in §6.10.2 to distinguish sequence types (linear scan) from hash-based containers (hash lookup). Add a note acknowledging that the x is e check has observable consequences for NaN and state-dependent __eq__, and that object identity is unspecified for non-literal expressions. Also add a clarifying sentence in §6.2.3.1 noting that non-literal expressions make no identity guarantees. --- Doc/reference/expressions.rst | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Doc/reference/expressions.rst b/Doc/reference/expressions.rst index 68dcfc00bbd99c3..63d12fa219e5d5f 100644 --- a/Doc/reference/expressions.rst +++ b/Doc/reference/expressions.rst @@ -256,6 +256,9 @@ both their structure and the *identity* of the values match. Currently, each evaluation of a template string results in a different object. +Other expressions which are not literals (such as calls, +comprehensions, and attribute accesses) likewise make no guarantees +about object identity across separate evaluations. .. _string-concatenation: @@ -1977,9 +1980,22 @@ The operators :keyword:`in` and :keyword:`not in` test for membership. ``x in s`` evaluates to ``True`` if *x* is a member of *s*, and ``False`` otherwise. ``x not in s`` returns the negation of ``x in s``. All built-in sequences and set types support this as well as dictionary, for which :keyword:`!in` tests -whether the dictionary has a given key. For container types such as list, tuple, -set, frozenset, dict, or collections.deque, the expression ``x in y`` is equivalent -to ``any(x is e or x == e for e in y)``. +whether the dictionary has a given key. For sequence types such as list, tuple, or collections.deque, +the expression ``x in y`` is equivalent to ``any(x is e or x == e for e in y)``. + +For hash-based container types such as set, frozenset, and dict, +``x in y`` first hashes *x* to locate a candidate element *e*, then +returns ``True`` if ``x is e or x == e``. This is equivalent to the +sequence case under the assumption that equal objects have the same +hash value. + +.. note:: + + The ``x is e`` check means that for objects where ``x != x`` + (such as NaN) or where ``__eq__`` is state-dependent, membership + can succeed via identity even when equality fails. This behavior + is not guaranteed across implementations, as object identity for + repeated evaluations of non-literal expressions is unspecified. For the string and bytes types, ``x in y`` is ``True`` if and only if *x* is a substring of *y*. An equivalent test is ``y.find(x) != -1``. Empty strings are