跳转至

尚未翻译

本页面尚未翻译成中文,因此显示英文原文。 帮助翻译

enum Module Complexity

The enum module binds symbolic names to constant values. Almost everything it does costs once, when the class body runs: the metaclass builds the members, indexes them by name and by value, and freezes the result.

n is the number of member names, including aliases. Name lookup uses a dictionary; value lookup uses a dictionary for hashable values and scans for unhashable values. Lookup bounds assume constant-cost hashing and equality and exclude custom _missing_() work. Construction bounds below cover ordinary hashable values.

Value lookup depends on hashability

Color(1) uses the reverse value dictionary, giving expected O(1) lookup. List- and dict-valued enums are supported too, but looking up an unhashable value requires up to O(n) equality comparisons before a match or a call to _missing_().

Complexity Reference

Building an enum

Operation Time Space Notes
class C(enum.Enum) — the class body O(n) O(n) n = members; each is instantiated and indexed by name and by value
enum.Enum(name, names) — the functional API O(n) O(n) Builds the same class at run time
enum.auto() O(1) O(1) A sentinel resolved by _generate_next_value_ when the class is built
enum.member(obj), enum.nonmember(obj) O(1) O(1) Python 3.11+; force a class-body name to be, or not be, a member
enum.property O(1) O(1) Python 3.11+; the descriptor behind .name and .value, which shadows a member of the same name
enum.EnumDict O(1) O(1) Python 3.13+; the mapping the class body is executed in
enum.EnumDict.member_names O(n) O(n) Python 3.13+; a fresh list of the namespace's member names, including aliases, on each access

Looking members up

Operation Time Space Notes
C.MEMBER O(1) O(1) An attribute lookup on the class
C['MEMBER'] O(1) O(1) _member_map_, a dict keyed by name
C(value) O(1) expected for hashable values; O(n) comparisons for unhashable values O(1) auxiliary A failed lookup calls _missing_(); comparison and hook costs are additional
member.name, member.value O(1) O(1) Stored on the member
len(C) O(1) O(1) The canonical member list's length
iter(C) O(n) O(1) Canonical members only — aliases are skipped
C.__members__ O(1) O(1) auxiliary Read-only proxy over the existing dictionary, including aliases; copying it to a dict or list costs O(n) time and space
x in C O(1) for a member; up to O(n) comparisons for raw values from 3.13 O(1) auxiliary, excluding hooks A raw value raises TypeError before 3.12; from 3.12 a hashable one answers True/False, while an unhashable one still raises until 3.13 compares it

Variants

Operation Time Space Notes
enum.IntEnum, enum.StrEnum O(1) O(1) Members are ints and strs, so they compare and format as one; StrEnum is 3.11+
enum.ReprEnum O(1) O(1) Python 3.11+; the base that keeps the mixed-in type's __str__ and __format__
enum.Flag, enum.IntFlag O(1) O(1) Members are powers of two, so \| and & are single integer operations
Iterating or len() on a combined flag O(b) O(1) b = bits set; Python 3.11+, where a combination became sized and iterable
enum.EnumMeta, enum.EnumType O(n) O(n) The metaclass that does the building; EnumType is the 3.11+ name for the same object

Validation and helpers

Operation Time Space Notes
enum.unique(cls) O(n) O(a) a = aliases found; raises ValueError if there are any
enum.verify(*checks) O(n) O(n) Python 3.11+; a class decorator running the EnumCheck constraints
enum.EnumCheckenum.UNIQUE, enum.CONTINUOUS, enum.NAMED_FLAGS O(1) O(1) Python 3.11+; the constraints verify() applies
enum.FlagBoundaryenum.STRICT, enum.CONFORM, enum.EJECT, enum.KEEP O(1) O(1) Python 3.11+; what a flag does with bits no member claims
enum.global_enum(cls) O(n) O(n) Python 3.11+; exports members into the defining module and changes their string representations
enum.global_str(member) O(1) for a named member O(1) auxiliary for a named member Python 3.11+; returns the stored name; unnamed members instead incur value-formatting costs
enum.show_flag_values(value) O(b·w) bit work O(b·w) bits Python 3.11+; returns b powers of two in ascending order; w = positive input's bit length; zero takes O(1)
enum.bin(num, max_bits=None) O(w) formatting plus integer exponentiation O(w) Python 3.11+; w = output width including padding; also computes 2 ** num.bit_length()
enum.global_enum_repr(self), enum.global_flag_repr(self) O(1) O(1) Python 3.11+; the repr that names the module rather than the class
enum.pickle_by_global_name(self, proto), enum.pickle_by_enum_name(self, proto) O(1) O(1) Python 3.11+; __reduce_ex__ implementations

Enum Basics

Creating an Enum

from enum import Enum

# The class body runs once - O(n) in members
class Color(Enum):
    RED = 1
    GREEN = 2
    BLUE = 3

assert Color.RED.name == 'RED'    # O(1)
assert Color.RED.value == 1       # O(1)
assert str(Color.RED) == 'Color.RED'

Name and Value Lookup

from enum import Enum

class Status(Enum):
    PENDING = 'pending'
    ACTIVE = 'active'
    DONE = 'done'

assert Status.ACTIVE is Status['ACTIVE']    # O(1) - by name
assert Status.ACTIVE is Status('active')    # O(1) expected - hashable value

Lists = Enum('Lists', {'FIRST': [1], 'SECOND': [2]})
assert Lists([2]) is Lists.SECOND           # O(n) comparisons - unhashable value

# A value with no member raises, having consulted the map and then _missing_
try:
    Status('missing')
except ValueError as error:
    assert 'is not a valid Status' in str(error)

Members Are Singletons

There is exactly one object per member, so is is the right comparison and identity checks cost nothing.

from enum import Enum

class Color(Enum):
    RED = 1

assert Color(1) is Color.RED
assert Color(1) is Color['RED']
assert Color.RED == Color.RED and Color.RED is Color.RED

# An Enum member is not its value
assert Color.RED != 1

Iteration Skips Aliases

A second name for the same value is an alias. It is reachable by name and appears in __members__, but iteration and len() see only the canonical members.

from enum import Enum

class Color(Enum):
    RED = 1
    CRIMSON = 1   # an alias for RED
    GREEN = 2

assert Color.CRIMSON is Color.RED

assert [c.name for c in Color] == ['RED', 'GREEN']   # O(n), aliases skipped
assert len(Color) == 2                                # O(1)
assert list(Color.__members__) == ['RED', 'CRIMSON', 'GREEN']  # aliases included

Membership

import sys
from enum import Enum

class Color(Enum):
    RED = 1

assert Color.RED in Color   # O(1) on every version

# For a plain value the answer changed in 3.12
if sys.version_info >= (3, 12):
    assert (1 in Color) is True
    assert (99 in Color) is False

value in EnumClass was not always a question you could ask

On 3.10 and 3.11 testing a non-member raises TypeError (with a DeprecationWarning saying the behaviour will change). Python 3.12 answers for a hashable value but still raises TypeError: unhashable type for a list- or dict-valued member; 3.13 is the first release that compares one. For hashable values, use value in EnumClass._value2member_map_ to support the older versions, or catch the TypeError.

Integer and String Enums

IntEnum and StrEnum members are ints and strs, so they interoperate with code that has never heard of the enum — at the cost of comparing equal to a bare value.

from enum import IntEnum

class Priority(IntEnum):
    LOW = 1
    HIGH = 3

assert Priority.HIGH > Priority.LOW    # O(1) - an int comparison
assert Priority.HIGH == 3              # unlike a plain Enum
assert Priority.HIGH + 1 == 4
assert sorted(Priority) == [Priority.LOW, Priority.HIGH]

Flags

Flag members are powers of two, so combining and testing them are single integer operations. A combination is iterable and sized from Python 3.11.

import sys
from enum import Flag, auto

class Permission(Flag):
    READ = auto()
    WRITE = auto()
    EXECUTE = auto()

combined = Permission.READ | Permission.WRITE   # O(1)

assert Permission.READ in combined              # O(1) - a bitwise test
assert Permission.EXECUTE not in combined
assert combined & Permission.READ == Permission.READ

# A combination is looked up by value like any other member
assert Permission(combined.value) is combined

if sys.version_info >= (3, 11):
    assert len(combined) == 2                        # O(b) in bits set
    assert {p.name for p in combined} == {'READ', 'WRITE'}

Validation

unique() walks the members once and refuses aliases. verify() (3.11+) generalizes that to the other constraints.

import sys
from enum import Enum, unique

# unique() rejects an alias - O(n)
try:
    @unique
    class Duplicated(Enum):
        A = 1
        B = 1
except ValueError as error:
    assert 'duplicate values' in str(error)

if sys.version_info >= (3, 11):
    from enum import CONTINUOUS, verify

    # CONTINUOUS refuses a gap in the values - O(n)
    try:
        @verify(CONTINUOUS)
        class Gapped(Enum):
            A = 1
            C = 3
    except ValueError as error:
        assert 'invalid enum' in str(error) or 'are missing' in str(error)

The Functional API

Enum(name, names) builds the same class the class statement would, at the same O(n) — just later.

from enum import Enum

Color = Enum('Color', ['RED', 'GREEN', 'BLUE'])   # O(n)

assert Color.RED.value == 1        # auto-numbered from 1
assert len(Color) == 3
assert Color(1) is Color.RED       # O(1) expected - hashable value

# A mapping gives explicit values
Status = Enum('Status', {'OK': 200, 'MISSING': 404})
assert Status(404) is Status.MISSING   # O(1)

Methods on an Enum

Methods and non-member attributes live on the class, not among the members, so adding them does not change any bound.

from enum import Enum

class Planet(Enum):
    MERCURY = (3.303e23, 2.4397e6)
    EARTH = (5.976e24, 6.37814e6)

    def __init__(self, mass, radius):
        self.mass = mass
        self.radius = radius

    @property
    def surface_gravity(self):
        return 6.67300E-11 * self.mass / (self.radius * self.radius)

assert len(Planet) == 2                     # the property is not a member
assert round(Planet.EARTH.surface_gravity, 2) == 9.80

Version Notes

  • Python 3.11+: StrEnum, ReprEnum, EnumType, verify with EnumCheck, FlagBoundary, member/nonmember, enum.property, show_flag_values, enum.bin, the global_* and pickle_by_* helpers; a combined Flag became sized and iterable
  • Python 3.12+: value in EnumClass answers True/False instead of raising TypeError
  • Python 3.13+: EnumDict, the class-body mapping, became public
  • dataclasses - the other decorator that builds methods at import
  • typing - Literal as the alternative when you want no runtime object at all
  • collections - namedtuple for a fixed record rather than a fixed set

Best Practices

Do:

  • Use C(value) for value lookup; hashable values use the reverse dictionary
  • Compare members with is; there is exactly one object per member
  • Use IntEnum or StrEnum only where the value has to cross an API that wants a plain int or str
  • Reach for unique() when the values come from somewhere you do not control

Avoid:

  • Building an enum inside a function that runs often — the class body is the O(n) part
  • Iterating to find a member by value
  • Assuming __members__ and iteration agree; aliases appear in one and not the other
  • value in EnumClass if you still support 3.10 or 3.11