https://github.com/python/cpython/commit/1016f735349410e4caf0dc20b52b0c1a3e150dda
commit: 1016f735349410e4caf0dc20b52b0c1a3e150dda
branch: main
author: Ethan Furman <[email protected]>
committer: ethanfurman <[email protected]>
date: 2026-10-19T12:52:54-07:00
summary:
gh-158856: convert `Enum._hashable_values_` to a set (GH-159250)
files:
A Misc/NEWS.d/next/Library/2026-10-12-11-36-48.gh-issue-158856.jLR-Lv.rst
M Lib/enum.py
diff --git a/Lib/enum.py b/Lib/enum.py
index 076aa18a02fd20..8a6519e006515a 100644
--- a/Lib/enum.py
+++ b/Lib/enum.py
@@ -34,6 +34,16 @@ class member(object):
def __init__(self, value):
self.value = value
+def _hashable(obj):
+ """
+ Returns True if obj is hashable, False otherwise.
+ """
+ try:
+ hash(obj)
+ return True
+ except TypeError:
+ return False
+
def _is_descriptor(obj):
"""
Returns True if obj is a descriptor, False otherwise.
@@ -313,14 +323,13 @@ def __set_name__(self, enum_class, member_name):
enum_class._add_member_(member_name, enum_member)
try:
- # This may fail if value is not hashable. We can't add the value
- # to the map, and by-value lookups for this value will be
- # linear.
+ # This may fail if value is not hashable.
enum_class._value2member_map_.setdefault(value, enum_member)
- if value not in enum_class._hashable_values_:
- enum_class._hashable_values_.append(value)
+ enum_class._hashable_values_.add(value)
except TypeError:
- # keep track of the value in a list so containment checks are quick
+ # We can't add the value to the map; by-value lookups for this
+ # value will be linear. keep track of the value in a list so
+ # containment checks are quicker
enum_class._unhashable_values_.append(value)
enum_class._unhashable_values_map_.setdefault(member_name,
[]).append(value)
@@ -530,7 +539,7 @@ def __new__(metacls, cls, bases, classdict, *,
boundary=None, _simple=False, **k
classdict['_member_names_'] = []
classdict['_member_map_'] = {}
classdict['_value2member_map_'] = {}
- classdict['_hashable_values_'] = [] # for comparing with
non-hashable types
+ classdict['_hashable_values_'] = set() # for comparing with
non-hashable types
classdict['_unhashable_values_'] = [] # e.g. frozenset() with
set()
classdict['_unhashable_values_map_'] = {}
classdict['_member_type_'] = member_type
@@ -767,10 +776,10 @@ def __contains__(cls, value):
return isinstance(result, cls)
except ValueError:
pass
- return (
- value in cls._unhashable_values_ # both structures are lists
- or value in cls._hashable_values_
- )
+ try:
+ return value in cls._hashable_values_ # a set
+ except TypeError:
+ return value in cls._unhashable_values_ # a list
def __delattr__(cls, attr):
# nicer error message when someone tries to delete an attribute
@@ -1261,7 +1270,7 @@ def _add_value_alias_(self, value):
# to the map, and by-value lookups for this value will be
# linear.
cls._value2member_map_.setdefault(value, self)
- cls._hashable_values_.append(value)
+ cls._hashable_values_.add(value)
except TypeError:
# keep track of the value in a list so containment checks are quick
cls._unhashable_values_.append(value)
@@ -1802,7 +1811,7 @@ def convert_class(cls):
body['_member_names_'] = member_names = []
body['_member_map_'] = member_map = {}
body['_value2member_map_'] = value2member_map = {}
- body['_hashable_values_'] = hashable_values = []
+ body['_hashable_values_'] = hashable_values = set()
body['_unhashable_values_'] = unhashable_values = []
body['_unhashable_values_map_'] = {}
body['_member_type_'] = member_type = etype._member_type_
@@ -1866,7 +1875,7 @@ def convert_class(cls):
contained = value2member_map.get(member._value_)
except TypeError:
contained = None
- if member._value_ in unhashable_values or member.value in
hashable_values:
+ if member._value_ in hashable_values or member.value in
unhashable_values:
for m in enum_class:
if m._value_ == member._value_:
contained = m
@@ -1886,7 +1895,7 @@ def convert_class(cls):
else:
enum_class._add_member_(name, member)
value2member_map[value] = member
- hashable_values.append(value)
+ hashable_values.add(value)
if _is_single_bit(value):
# not a multi-bit alias, record in _member_names_ and
_flag_mask_
member_names.append(name)
@@ -1923,7 +1932,9 @@ def convert_class(cls):
contained = value2member_map.get(member._value_)
except TypeError:
contained = None
- if member._value_ in unhashable_values or member._value_
in hashable_values:
+ if (_hashable(member._value_) and member._value_ in
hashable_values
+ or member._value_ in unhashable_values
+ ):
for m in enum_class:
if m._value_ == member._value_:
contained = m
@@ -1949,8 +1960,7 @@ def convert_class(cls):
# to the map, and by-value lookups for this value will
be
# linear.
enum_class._value2member_map_.setdefault(value, member)
- if value not in hashable_values:
- hashable_values.append(value)
+ hashable_values.add(value)
except TypeError:
# keep track of the value in a list so containment
checks are quick
enum_class._unhashable_values_.append(value)
diff --git
a/Misc/NEWS.d/next/Library/2026-10-12-11-36-48.gh-issue-158856.jLR-Lv.rst
b/Misc/NEWS.d/next/Library/2026-10-12-11-36-48.gh-issue-158856.jLR-Lv.rst
new file mode 100644
index 00000000000000..a95c7b0cb44af3
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-10-12-11-36-48.gh-issue-158856.jLR-Lv.rst
@@ -0,0 +1 @@
+Improve large Enum creation performance.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]