Skip to content

Commit 1016f73

Browse files
authored
gh-158856: convert Enum._hashable_values_ to a set (GH-159250)
1 parent 5c75b50 commit 1016f73

2 files changed

Lines changed: 29 additions & 18 deletions

File tree

‎Lib/enum.py‎

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ class member(object):
3434
def __init__(self, value):
3535
self.value = value
3636

37+
def _hashable(obj):
38+
"""
39+
Returns True if obj is hashable, False otherwise.
40+
"""
41+
try:
42+
hash(obj)
43+
return True
44+
except TypeError:
45+
return False
46+
3747
def _is_descriptor(obj):
3848
"""
3949
Returns True if obj is a descriptor, False otherwise.
@@ -313,14 +323,13 @@ def __set_name__(self, enum_class, member_name):
313323

314324
enum_class._add_member_(member_name, enum_member)
315325
try:
316-
# This may fail if value is not hashable. We can't add the value
317-
# to the map, and by-value lookups for this value will be
318-
# linear.
326+
# This may fail if value is not hashable.
319327
enum_class._value2member_map_.setdefault(value, enum_member)
320-
if value not in enum_class._hashable_values_:
321-
enum_class._hashable_values_.append(value)
328+
enum_class._hashable_values_.add(value)
322329
except TypeError:
323-
# keep track of the value in a list so containment checks are quick
330+
# We can't add the value to the map; by-value lookups for this
331+
# value will be linear. keep track of the value in a list so
332+
# containment checks are quicker
324333
enum_class._unhashable_values_.append(value)
325334
enum_class._unhashable_values_map_.setdefault(member_name, []).append(value)
326335

@@ -530,7 +539,7 @@ def __new__(metacls, cls, bases, classdict, *, boundary=None, _simple=False, **k
530539
classdict['_member_names_'] = []
531540
classdict['_member_map_'] = {}
532541
classdict['_value2member_map_'] = {}
533-
classdict['_hashable_values_'] = [] # for comparing with non-hashable types
542+
classdict['_hashable_values_'] = set() # for comparing with non-hashable types
534543
classdict['_unhashable_values_'] = [] # e.g. frozenset() with set()
535544
classdict['_unhashable_values_map_'] = {}
536545
classdict['_member_type_'] = member_type
@@ -767,10 +776,10 @@ def __contains__(cls, value):
767776
return isinstance(result, cls)
768777
except ValueError:
769778
pass
770-
return (
771-
value in cls._unhashable_values_ # both structures are lists
772-
or value in cls._hashable_values_
773-
)
779+
try:
780+
return value in cls._hashable_values_ # a set
781+
except TypeError:
782+
return value in cls._unhashable_values_ # a list
774783

775784
def __delattr__(cls, attr):
776785
# nicer error message when someone tries to delete an attribute
@@ -1261,7 +1270,7 @@ def _add_value_alias_(self, value):
12611270
# to the map, and by-value lookups for this value will be
12621271
# linear.
12631272
cls._value2member_map_.setdefault(value, self)
1264-
cls._hashable_values_.append(value)
1273+
cls._hashable_values_.add(value)
12651274
except TypeError:
12661275
# keep track of the value in a list so containment checks are quick
12671276
cls._unhashable_values_.append(value)
@@ -1802,7 +1811,7 @@ def convert_class(cls):
18021811
body['_member_names_'] = member_names = []
18031812
body['_member_map_'] = member_map = {}
18041813
body['_value2member_map_'] = value2member_map = {}
1805-
body['_hashable_values_'] = hashable_values = []
1814+
body['_hashable_values_'] = hashable_values = set()
18061815
body['_unhashable_values_'] = unhashable_values = []
18071816
body['_unhashable_values_map_'] = {}
18081817
body['_member_type_'] = member_type = etype._member_type_
@@ -1866,7 +1875,7 @@ def convert_class(cls):
18661875
contained = value2member_map.get(member._value_)
18671876
except TypeError:
18681877
contained = None
1869-
if member._value_ in unhashable_values or member.value in hashable_values:
1878+
if member._value_ in hashable_values or member.value in unhashable_values:
18701879
for m in enum_class:
18711880
if m._value_ == member._value_:
18721881
contained = m
@@ -1886,7 +1895,7 @@ def convert_class(cls):
18861895
else:
18871896
enum_class._add_member_(name, member)
18881897
value2member_map[value] = member
1889-
hashable_values.append(value)
1898+
hashable_values.add(value)
18901899
if _is_single_bit(value):
18911900
# not a multi-bit alias, record in _member_names_ and _flag_mask_
18921901
member_names.append(name)
@@ -1923,7 +1932,9 @@ def convert_class(cls):
19231932
contained = value2member_map.get(member._value_)
19241933
except TypeError:
19251934
contained = None
1926-
if member._value_ in unhashable_values or member._value_ in hashable_values:
1935+
if (_hashable(member._value_) and member._value_ in hashable_values
1936+
or member._value_ in unhashable_values
1937+
):
19271938
for m in enum_class:
19281939
if m._value_ == member._value_:
19291940
contained = m
@@ -1949,8 +1960,7 @@ def convert_class(cls):
19491960
# to the map, and by-value lookups for this value will be
19501961
# linear.
19511962
enum_class._value2member_map_.setdefault(value, member)
1952-
if value not in hashable_values:
1953-
hashable_values.append(value)
1963+
hashable_values.add(value)
19541964
except TypeError:
19551965
# keep track of the value in a list so containment checks are quick
19561966
enum_class._unhashable_values_.append(value)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve large Enum creation performance.

0 commit comments

Comments
 (0)