diff options
author | Carl Friedrich Bolz-Tereick <cfbolz@gmx.de> | 2021-10-14 20:59:51 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-14 20:59:51 (GMT) |
commit | b2af211e229df941d9b404f69547a264115156b5 (patch) | |
tree | 7419794a48171aa351ce7ebb1510bcf6408b2cde | |
parent | 0bbea0723ee07f9d7ad9745f0e1875718ef38715 (diff) | |
download | cpython-b2af211e229df941d9b404f69547a264115156b5.zip cpython-b2af211e229df941d9b404f69547a264115156b5.tar.gz cpython-b2af211e229df941d9b404f69547a264115156b5.tar.bz2 |
bpo-45417: [Enum] fix quadratic behavior during creation (GH-28907)
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.
-rw-r--r-- | Lib/enum.py | 21 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2021-10-12-20-35-06.bpo-45417.gQM-O7.rst | 2 |
2 files changed, 16 insertions, 7 deletions
diff --git a/Lib/enum.py b/Lib/enum.py index 0776761..461d276 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -235,11 +235,18 @@ class _proto_member: 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 ( @@ -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 @@ -365,7 +372,7 @@ class _EnumDict(dict): ) 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) diff --git a/Misc/NEWS.d/next/Library/2021-10-12-20-35-06.bpo-45417.gQM-O7.rst b/Misc/NEWS.d/next/Library/2021-10-12-20-35-06.bpo-45417.gQM-O7.rst new file mode 100644 index 0000000..a15c239 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-10-12-20-35-06.bpo-45417.gQM-O7.rst @@ -0,0 +1,2 @@ +Fix quadratic behaviour in the enum module: Creation of enum classes with a +lot of entries was quadratic. |