Skip to content

Commit

Permalink
bpo-45417: [Enum] fix quadratic behavior during creation (GH-28907)
Browse files Browse the repository at this point in the history
Creating an Enum exhibited quadratic behavior based on the number of members in three places:
- `EnumDict._member_names`: a list searched with each new member's name
- member creation: a `for` loop checking each existing member to see if new member was a duplicate
- `auto()` values: a list of all previous values in enum was copied before being sent to `_generate_next_value()`

Two of those issues have been resolved:
- `_EnumDict._member_names` is now a dictionary so lookups are fast
- member creation tries a fast value lookup before falling back to the slower `for` loop lookup

The third issue still remains, as `_generate_next_value_()` can be user-overridden and could corrupt the last values list if it were not copied.
  • Loading branch information
cfbolz authored Oct 14, 2021
1 parent 0bbea07 commit b2af211
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 7 deletions.
21 changes: 14 additions & 7 deletions Lib/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,18 @@ def __set_name__(self, enum_class, member_name):
enum_member._sort_order_ = len(enum_class._member_names_)
# If another member with the same value was already defined, the
# new member becomes an alias to the existing one.
for name, canonical_member in enum_class._member_map_.items():
if canonical_member._value_ == enum_member._value_:
enum_member = canonical_member
break
else:
try:
try:
# try to do a fast lookup to avoid the quadratic loop
enum_member = enum_class._value2member_map_[value]
except TypeError:
for name, canonical_member in enum_class._member_map_.items():
if canonical_member._value_ == value:
enum_member = canonical_member
break
else:
raise KeyError
except KeyError:
# this could still be an alias if the value is multi-bit and the
# class is a flag class
if (
Expand Down Expand Up @@ -301,7 +308,7 @@ class _EnumDict(dict):
"""
def __init__(self):
super().__init__()
self._member_names = []
self._member_names = {} # use a dict to keep insertion order
self._last_values = []
self._ignore = []
self._auto_called = False
Expand Down Expand Up @@ -365,7 +372,7 @@ def __setitem__(self, key, value):
)
self._auto_called = True
value = value.value
self._member_names.append(key)
self._member_names[key] = None
self._last_values.append(value)
super().__setitem__(key, value)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix quadratic behaviour in the enum module: Creation of enum classes with a
lot of entries was quadratic.

0 comments on commit b2af211

Please sign in to comment.