This PEP proposes adding an enumeration type to the Python standard library.
An enumeration is a set of symbolic names bound to unique, constant values.Within an enumeration, the values can be compared by identity, and theenumeration itself can be iterated over.
The idea of adding an enum type to Python is not new -PEP 354 is aprevious attempt that was rejected in 2005. Recently a new set of discussionswas initiated[3] on thepython-ideas mailing list. Many new ideas wereproposed in several threads; after a lengthy discussion Guido proposed addingflufl.enum to the standard library[4]. During the PyCon 2013 languagesummit the issue was discussed further. It became clear that many developerswant to see an enum that subclassesint, which can allow us to replacemany integer constants in the standard library by enums with friendly stringrepresentations, without ceding backwards compatibility. An additionaldiscussion among several interested core developers led to the proposal ofhavingIntEnum as a special case ofEnum.
The key dividing issue betweenEnum andIntEnum is whether comparingto integers is semantically meaningful. For most uses of enumerations, it’safeature to reject comparison to integers; enums that compare to integerslead, through transitivity, to comparisons between enums of unrelated types,which isn’t desirable in most cases. For some uses, however, greaterinteroperability with integers is desired. For instance, this is the case forreplacing existing standard library constants (such assocket.AF_INET)with enumerations.
Further discussion in late April 2013 led to the conclusion that enumerationmembers should belong to the type of their enum:type(Color.red)==Color.Guido has pronounced a decision on this issue[5], as well as related issuesof not allowing to subclass enums[6], unless they define no enumerationmembers[7].
The PEP was accepted by Guido on May 10th, 2013[1].
[Based partly on the Motivation stated inPEP 354]
The properties of an enumeration are useful for defining an immutable, relatedset of constant values that may or may not have a semantic meaning. Classicexamples are days of the week (Sunday through Saturday) and school assessmentgrades (‘A’ through ‘D’, and ‘F’). Other examples include error status valuesand states within a defined process.
It is possible to simply define a sequence of values of some other basic type,such asint orstr, to represent discrete arbitrary values. However,an enumeration ensures that such values are distinct from any others including,importantly, values within other enumerations, and that operations withoutmeaning (“Wednesday times two”) are not defined for these values. It alsoprovides a convenient printable representation of enum values without requiringtedious repetition while defining them (i.e. noGREEN='green').
We propose to add a module namedenum to the standard library. The maintype exposed by this module isEnum. Hence, to import theEnum typeuser code will run:
>>>fromenumimportEnum
Enumerations are created using the class syntax, which makes them easy to readand write. An alternative creation method is described inFunctional API.To define an enumeration, subclassEnum as follows:
>>>fromenumimportEnum>>>classColor(Enum):...red=1...green=2...blue=3
A note on nomenclature: we callColor anenumeration (orenum)andColor.red,Color.green areenumeration members (orenum members). Enumeration members also havevalues (the value ofColor.red is1, etc.)
Enumeration members have human readable string representations:
>>>print(Color.red)Color.red
…while theirrepr has more information:
>>>print(repr(Color.red))<Color.red: 1>
Thetype of an enumeration member is the enumeration it belongs to:
>>>type(Color.red)<Enum 'Color'>>>>isinstance(Color.green,Color)True>>>
Enums also have a property that contains just their item name:
>>>print(Color.red.name)red
Enumerations support iteration, in definition order:
>>>classShake(Enum):...vanilla=7...chocolate=4...cookies=9...mint=3...>>>forshakeinShake:...print(shake)...Shake.vanillaShake.chocolateShake.cookiesShake.mint
Enumeration members are hashable, so they can be used in dictionaries and sets:
>>>apples={}>>>apples[Color.red]='red delicious'>>>apples[Color.green]='granny smith'>>>apples{<Color.red: 1>: 'red delicious', <Color.green: 2>: 'granny smith'}
Sometimes it’s useful to access members in enumerations programmatically (i.e.situations whereColor.red won’t do because the exact color is not knownat program-writing time).Enum allows such access:
>>>Color(1)<Color.red: 1>>>>Color(3)<Color.blue: 3>
If you want to access enum members byname, use item access:
>>>Color['red']<Color.red: 1>>>>Color['green']<Color.green: 2>
Having two enum members with the same name is invalid:
>>>classShape(Enum):...square=2...square=3...Traceback (most recent call last):...TypeError:Attempted to reuse key: square
However, two enum members are allowed to have the same value. Given two membersA and B with the same value (and A defined first), B is an alias to A. By-valuelookup of the value of A and B will return A. By-name lookup of B will alsoreturn A:
>>>classShape(Enum):...square=2...diamond=1...circle=3...alias_for_square=2...>>>Shape.square<Shape.square: 2>>>>Shape.alias_for_square<Shape.square: 2>>>>Shape(2)<Shape.square: 2>
Iterating over the members of an enum does not provide the aliases:
>>>list(Shape)[<Shape.square: 2>, <Shape.diamond: 1>, <Shape.circle: 3>]
The special attribute__members__ is an ordered dictionary mapping namesto members. It includes all names defined in the enumeration, including thealiases:
>>>forname,memberinShape.__members__.items():...name,member...('square', <Shape.square: 2>)('diamond', <Shape.diamond: 1>)('circle', <Shape.circle: 3>)('alias_for_square', <Shape.square: 2>)
The__members__ attribute can be used for detailed programmatic access tothe enumeration members. For example, finding all the aliases:
>>>[nameforname,memberinShape.__members__.items()ifmember.name!=name]['alias_for_square']
Enumeration members are compared by identity:
>>>Color.redisColor.redTrue>>>Color.redisColor.blueFalse>>>Color.redisnotColor.blueTrue
Ordered comparisons between enumeration values arenot supported. Enums arenot integers (but seeIntEnum below):
>>>Color.red<Color.blueTraceback (most recent call last): File"<stdin>", line1, in<module>TypeError:unorderable types: Color() < Color()
Equality comparisons are defined though:
>>>Color.blue==Color.redFalse>>>Color.blue!=Color.redTrue>>>Color.blue==Color.blueTrue
Comparisons against non-enumeration values will always compare not equal(again,IntEnum was explicitly designed to behave differently, seebelow):
>>>Color.blue==2False
The examples above use integers for enumeration values. Using integers isshort and handy (and provided by default by theFunctional API), but notstrictly enforced. In the vast majority of use-cases, one doesn’t care whatthe actual value of an enumeration is. But if the valueis important,enumerations can have arbitrary values.
Enumerations are Python classes, and can have methods and special methods asusual. If we have this enumeration:
classMood(Enum):funky=1happy=3defdescribe(self):# self is the member herereturnself.name,self.valuedef__str__(self):return'my custom str!{0}'.format(self.value)@classmethoddeffavorite_mood(cls):# cls here is the enumerationreturncls.happy
Then:
>>>Mood.favorite_mood()<Mood.happy: 3>>>>Mood.happy.describe()('happy', 3)>>>str(Mood.funky)'my custom str! 1'
The rules for what is allowed are as follows: all attributes defined within anenumeration will become members of this enumeration, with the exception of__dunder__ names and descriptors[9]; methods are descriptors too.
Subclassing an enumeration is allowed only if the enumeration does not defineany members. So this is forbidden:
>>>classMoreColor(Color):...pink=17...TypeError: Cannot extend enumerations
But this is allowed:
>>>classFoo(Enum):...defsome_behavior(self):...pass...>>>classBar(Foo):...happy=1...sad=2...
The rationale for this decision was given by Guido in[6]. Allowing tosubclass enums that define members would lead to a violation of someimportant invariants of types and instances. On the other hand, itmakes sense to allow sharing some common behavior between a group ofenumerations, and subclassing empty enumerations is also used to implementIntEnum.
A variation ofEnum is proposed which is also a subclass ofint.Members of anIntEnum can be compared to integers; by extension,integer enumerations of different types can also be compared to each other:
>>>fromenumimportIntEnum>>>classShape(IntEnum):...circle=1...square=2...>>>classRequest(IntEnum):...post=1...get=2...>>>Shape==1False>>>Shape.circle==1True>>>Shape.circle==Request.postTrue
However they still can’t be compared toEnum:
>>>classShape(IntEnum):...circle=1...square=2...>>>classColor(Enum):...red=1...green=2...>>>Shape.circle==Color.redFalse
IntEnum values behave like integers in other ways you’d expect:
>>>int(Shape.circle)1>>>['a','b','c'][Shape.circle]'b'>>>[iforiinrange(Shape.square)][0, 1]
For the vast majority of code,Enum is strongly recommended,sinceIntEnum breaks some semantic promises of an enumeration (bybeing comparable to integers, and thus by transitivity to otherunrelated enumerations). It should be used only in special cases wherethere’s no other choice; for example, when integer constants arereplaced with enumerations and backwards compatibility is requiredwith code that still expects integers.
IntEnum will be part of theenum module. However, it would be verysimple to implement independently:
classIntEnum(int,Enum):pass
This demonstrates how similar derived enumerations can be defined, for exampleaStrEnum that mixes instr instead ofint.
Some rules:
IntEnum example above.int above.This restriction does not apply to mix-ins which only add methodsand don’t specify another data type such asint orstr.Enumerations can be pickled and unpickled:
>>>fromenum.tests.fruitimportFruit>>>frompickleimportdumps,loads>>>Fruit.tomatoisloads(dumps(Fruit.tomato))True
The usual restrictions for pickling apply: picklable enums must be defined inthe top level of a module, since unpickling requires them to be importablefrom that module.
TheEnum class is callable, providing the following functional API:
>>>Animal=Enum('Animal','ant bee cat dog')>>>Animal<Enum 'Animal'>>>>Animal.ant<Animal.ant: 1>>>>Animal.ant.value1>>>list(Animal)[<Animal.ant: 1>, <Animal.bee: 2>, <Animal.cat: 3>, <Animal.dog: 4>]
The semantics of this API resemblenamedtuple. The first argumentof the call toEnum is the name of the enumeration. Pickling enumscreated with the functional API will work on CPython and PyPy, but forIronPython and Jython you may need to specify the module name explicitlyas follows:
>>>Animals=Enum('Animals','ant bee cat dog',module=__name__)
The second argument is thesource of enumeration member names. It can be awhitespace-separated string of names, a sequence of names, a sequence of2-tuples with key/value pairs, or a mapping (e.g. dictionary) of names tovalues. The last two options enable assigning arbitrary values toenumerations; the others auto-assign increasing integers starting with 1. Anew class derived fromEnum is returned. In other words, the aboveassignment toAnimal is equivalent to:
>>>classAnimals(Enum):...ant=1...bee=2...cat=3...dog=4
The reason for defaulting to1 as the starting number and not0 isthat0 isFalse in a boolean sense, but enum members all evaluatetoTrue.
Some variations were proposed during the discussions in the mailing list.Here’s some of the more popular ones.
flufl.enum was the reference implementation upon which this PEP wasoriginally based. Eventually, it was decided against the inclusion offlufl.enum because its design separated enumeration members fromenumerations, so the former are not instances of the latter. Its designalso explicitly permits subclassing enumerations for extending them withmore members (due to the member/enum separation, the type invariants are notviolated influfl.enum with such a scheme).
Michael Foord proposed (and Tim Delaney provided a proof-of-conceptimplementation) to use metaclass magic that makes this possible:
classColor(Enum):red,green,blue
The values get actually assigned only when first looked up.
Pros: cleaner syntax that requires less typing for a very common task (justlisting enumeration names without caring about the values).
Cons: involves much magic in the implementation, which makes even thedefinition of such enums baffling when first seen. Besides, explicit isbetter than implicit.
A different approach to avoid specifying enum values is to use a special nameor form to auto assign them. For example:
classColor(Enum):red=None# auto-assigned to 0green=None# auto-assigned to 1blue=None# auto-assigned to 2
More flexibly:
classColor(Enum):red=7green=None# auto-assigned to 8blue=19purple=None# auto-assigned to 20
Some variations on this theme:
auto imported from the enum package....) instead ofNone to achieve thesame effect.Pros: no need to manually enter values. Makes it easier to change the enum andextend it, especially for large enumerations.
Cons: actually longer to type in many simple cases. The argument of explicitvs. implicit applies here as well.
The Python standard library has many places where the usage of enums would bebeneficial to replace other idioms currently used to represent them. Suchusages can be divided to two categories: user-code facing constants, andinternal constants.
User-code facing constants likeos.SEEK_*,socket module constants,decimal rounding modes and HTML error codes could require backwardscompatibility since user code may expect integers.IntEnum as describedabove provides the required semantics; being a subclass ofint, it does notaffect user code that expects integers, while on the other hand allowingprintable representations for enumeration values:
>>>importsocket>>>family=socket.AF_INET>>>family==2True>>>print(family)SocketFamily.AF_INET
Internal constants are not seen by user code but are employed internally bystdlib modules. These can be implemented withEnum. Some examplesuncovered by a very partial skim through the stdlib:binhex,imaplib,http/client,urllib/robotparser,idlelib,concurrent.futures,turtledemo.
In addition, looking at the code of the Twisted library, there are many usecases for replacing internal state constants with enums. The same can be saidabout a lot of networking code (especially implementation of protocols) andcan be seen in test protocols written with the Tulip library as well.
This PEP was initially proposing including theflufl.enum package[8]by Barry Warsaw into the stdlib, and is inspired in large parts by it.Ben Finney is the author of the earlier enumerationPEP 354.
Color.red would not beColor. (Discussion inhttps://mail.python.org/pipermail/python-dev/2013-April/125687.html)MoreColor.red andColor.red shouldnot be the same object, and on the otherisinstance checks becomeconfusing if they are not. The discussion also links to Stack Overflowdiscussions that make additional arguments.(https://mail.python.org/pipermail/python-dev/2013-April/125716.html)This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0435.rst
Last modified:2025-02-01 08:59:27 GMT