Enumerations

Theenum.Enum class behaves differently from other Python classes in severalways that require special-case handling in type checkers. This section discussesthe Enum behaviors that should be supported by type checkers and others whichmay be supported optionally. It is recommended that library and type stubauthors avoid using optional behaviors because these may not be supportedby some type checkers.

Enum Definition

Enum classes can be defined using a “class syntax” or a “function syntax”.The function syntax offers several ways to specify enum members: names passedas individual arguments, a list or tuple of names, a string ofcomma-delimited or space-delimited names, a list or tuple of tuples that containname/value pairs, and a dictionary of name/value items.

Type checkers should support the class syntax, but the function syntax (inits various forms) is optional:

classColor1(Enum):# SupportedRED=1GREEN=2BLUE=3Color2=Enum('Color2','RED','GREEN','BLUE')# OptionalColor3=Enum('Color3',['RED','GREEN','BLUE'])# OptionalColor4=Enum('Color4',('RED','GREEN','BLUE'))# OptionalColor5=Enum('Color5','RED, GREEN, BLUE')# OptionalColor6=Enum('Color6','RED GREEN BLUE')# OptionalColor7=Enum('Color7',[('RED',1),('GREEN',2),('BLUE',3)])# OptionalColor8=Enum('Color8',(('RED',1),('GREEN',2),('BLUE',3)))# OptionalColor9=Enum('Color9',{'RED':1,'GREEN':2,'BLUE':3})# Optional

Enum classes can also be defined using a subclass ofenum.Enum or any classthat usesenum.EnumType (or a subclass thereof) as a metaclass. Note thatenum.EnumType was namedenum.EnumMeta prior to Python 3.11. Typecheckers should treat such classes as enums:

classCustomEnum1(Enum):passclassColor7(CustomEnum1):# SupportedRED=1GREEN=2BLUE=3classCustomEnumType(EnumType):passclassCustomEnum2(metaclass=CustomEnumType):passclassColor8(CustomEnum2):# SupportedRED=1GREEN=2BLUE=3

Enum Behaviors

Enum classes are iterable and indexable, and they can be called with a valueto look up the enum member with that value. Type checkers should support thesebehaviors:

classColor(Enum):RED=1GREEN=2BLUE=3forcolorinColor:reveal_type(color)# Revealed type is 'Color'reveal_type(Color["RED"])# Revealed type is 'Literal[Color.RED]' (or 'Color')reveal_type(Color(3))# Revealed type is 'Literal[Color.BLUE]' (or 'Color')

Unlike most Python classes, Calling an enum class does not invoke its constructor.Instead, the call performs a value-based lookup of an enum member.

An Enum class with one or more defined members cannot be subclassed. They areimplicitly “final”. Type checkers should enforce this:

classEnumWithNoMembers(Enum):passclassShape(EnumWithNoMembers):# OK (because no members are defined)SQUARE=1CIRCLE=2classExtendedShape(Shape):# Type checker error: Shape is implicitly finalTRIANGLE=3

Defining Members

When using the “class syntax”, enum classes can define both members andother (non-member) attributes. TheEnumType metaclass applies a setof rules to distinguish between members and non-members. Type checkersshould honor the most common of these rules. The lesser-used rules areoptional. Some of these rules may be impossible to evaluate and enforcestatically in cases where dynamic values are used.

  • If an attribute is defined in the class body with a type annotation butwith no assigned value, a type checker should assume this is a non-memberattribute:

    classPet(Enum):genus:str# Non-member attributespecies:str# Non-member attributeCAT=1# Member attributeDOG=2# Member attribute

    Within a type stub, members can be defined using the actual runtime values,or a placeholder of... can be used:

    classPet(Enum):genus:str# Non-member attributespecies:str# Non-member attributeCAT=1# Member attribute with known value and typeDOG=cast(int,...)# Member attribute with unknown value and known typeBIRD=...# Member attribute with unknown value and type
  • Members defined within an enum class should not include explicit typeannotations. Type checkers should infer a literal type for all members.A type checker should report an error if a type annotation is usedfor an enum member because this type will be incorrect and misleadingto readers of the code:

    classPet(Enum):CAT=1# OKDOG:int=2# Type checker error
  • Methods, callables, descriptors (including properties), and nested classesthat are defined in the class are not treated as enum members by theEnumType metaclass and should likewise not be treated as enum members bya type checker:

    defidentity(x):returnxclassPet(Enum):CAT=1# Member attributeDOG=2# Member attributeconverter=lambdax:str(x)# Non-member attributetransform=identity# Non-member attribute@propertydefspecies(self)->str:# Non-member propertyreturn"mammal"defspeak(self)->None:# Non-member methodprint("meow"ifselfisPet.CATelse"woof")classNested:...# Non-member nested class
  • An attribute that is assigned the value of another member of the same enumis not a member itself. Instead, it is an alias for the first member:

    classTrafficLight(Enum):RED=1GREEN=2YELLOW=3AMBER=YELLOW# Alias for YELLOWreveal_type(TrafficLight.AMBER)# Revealed type is Literal[TrafficLight.YELLOW]
  • If using Python 3.11 or newer, theenum.member andenum.nonmemberclasses can be used to unambiguously distinguish members from non-members.Type checkers should support these classes:

    classExample(Enum):a=member(1)# Member attributeb=nonmember(2)# Non-member attribute@memberdefc(self)->None:# Member methodpassreveal_type(Example.a)# Revealed type is Literal[Example.a]reveal_type(Example.b)# Revealed type is int or Literal[2]reveal_type(Example.c)# Revealed type is Literal[Example.c]
  • An attribute with a private name (beginning with, but not ending in, a doubleunderscore) is treated as a non-member:

    classExample(Enum):A=1# Member attribute__B=2# Non-member attributereveal_type(Example.A)# Revealed type is Literal[Example.A]reveal_type(Example.__B)# Type Error: Private name is mangled
  • An enum class can define a class symbol named_ignore_. This can be a listof names or a string containing a space-delimited list of names that aredeleted from the enum class at runtime. Type checkers may support thismechanism:

    classPet(Enum):_ignore_="DOG FISH"CAT=1# Member attributeDOG=2# temporary variable, will be removed from the final enum classFISH=3# temporary variable, will be removed from the final enum class

Member Names

All enum member objects have an attribute_name_ that contains the member’sname. They also have a propertyname that returns the same name. Typecheckers may infer a literal type for the name of a member:

classColor(Enum):RED=1GREEN=2BLUE=3reveal_type(Color.RED._name_)# Revealed type is Literal["RED"] (or str)reveal_type(Color.RED.name)# Revealed type is Literal["RED"] (or str)deffunc1(red_or_blue:Literal[Color.RED,Color.BLUE]):reveal_type(red_or_blue.name)# Revealed type is Literal["RED", "BLUE"] (or str)deffunc2(any_color:Color):reveal_type(any_color.name)# Revealed type is Literal["RED", "BLUE", "GREEN"] (or str)

Member Values

All enum member objects have an attribute_value_ that contains the member’svalue. They also have a propertyvalue that returns the same value. Typecheckers may infer the type of a member’s value:

classColor(Enum):RED=1GREEN=2BLUE=3reveal_type(Color.RED._value_)# Revealed type is Literal[1] (or int or object or Any)reveal_type(Color.RED.value)# Revealed type is Literal[1] (or int or object or Any)deffunc1(red_or_blue:Literal[Color.RED,Color.BLUE]):reveal_type(red_or_blue.value)# Revealed type is Literal[1, 2] (or int or object or Any)deffunc2(any_color:Color):reveal_type(any_color.value)# Revealed type is Literal[1, 2, 3] (or int or object or Any)

The value of_value_ can be assigned in a constructor method. This techniqueis sometimes used to initialize both the member value and non-member attributes.If the value assigned in the class body is a tuple, the unpacked tuple value ispassed to the constructor. Type checkers may validate consistency between assignedtuple values and the constructor signature:

classPlanet(Enum):def__init__(self,value:int,mass:float,radius:float):self._value_=valueself.mass=massself.radius=radiusMERCURY=(1,3.303e+23,2.4397e6)VENUS=(2,4.869e+24,6.0518e6)EARTH=(3,5.976e+24,6.37814e6)MARS=(6.421e+23,3.3972e6)# Type checker error (optional)JUPITER=5# Type checker error (optional)reveal_type(Planet.MERCURY.value)# Revealed type is Literal[1] (or int or object or Any)

The classenum.auto and method_generate_next_value_ can be used withinan enum class to automatically generate values for enum members. Type checkersmay support these to infer literal types for member values:

classColor(Enum):RED=auto()GREEN=auto()BLUE=auto()reveal_type(Color.RED.value)# Revealed type is Literal[1] (or int or object or Any)

If an enum class provides an explicit type annotation for_value_, typecheckers should enforce this declared type when values are assigned to_value_:

classColor(Enum):_value_:intRED=1# OKGREEN="green"# Type errorclassPlanet(Enum):_value_:strdef__init__(self,value:int,mass:float,radius:float):self._value_=value# Type errorMERCURY=(1,3.303e+23,2.4397e6)

If the literal values for enum members are not supplied, as they sometimesare not within a type stub file, a type checker can use the type of the_value_ attribute:

classColumnType(Enum):_value_:intDORIC=...IONIC=...CORINTHIAN=...reveal_type(ColumnType.DORIC.value)# Revealed type is int (or object or Any)

Enum Literal Expansion

From the perspective of the type system, most enum classes are equivalentto the union of the literal members within that enum. (This ruledoes not apply to classes that derive fromenum.Flag because these enumsallow flags to be combined in arbitrary ways.) Because of the equivalencybetween an enum class and the union of literal members within that enum, thetwo types may be used interchangeably. Type checkers may therefore expandan enum type (that does not derive fromenum.Flag) into a union ofliteral values during type narrowing and exhaustion detection:

classColor(Enum):RED=1GREEN=2BLUE=3defprint_color1(c:Color):ifcisColor.REDorcisColor.BLUE:print("red or blue")else:reveal_type(c)# Revealed type is Literal[Color.GREEN]defprint_color2(c:Color):matchc:caseColor.RED|Color.BLUE:print("red or blue")caseColor.GREEN:print("green")case_:reveal_type(c)# Revealed type is Never

Likewise, a type checker should treat a complete union of all literal membersasequivalent to the enum type:

classAnswer(Enum):Yes=1No=2deffunc(val:object)->Answer:ifvalisnotAnswer.YesandvalisnotAnswer.No:raiseValueError("Invalid value")reveal_type(val)# Revealed type is Answer (or Literal[Answer.Yes, Answer.No])returnval# OK