summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCarl Friedrich Bolz-Tereick <cfbolz@gmx.de>2021-10-14 20:59:51 (GMT)
committerGitHub <noreply@github.com>2021-10-14 20:59:51 (GMT)
commitb2af211e229df941d9b404f69547a264115156b5 (patch)
tree7419794a48171aa351ce7ebb1510bcf6408b2cde
parent0bbea0723ee07f9d7ad9745f0e1875718ef38715 (diff)
downloadcpython-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.py21
-rw-r--r--Misc/NEWS.d/next/Library/2021-10-12-20-35-06.bpo-45417.gQM-O7.rst2
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.