Enum types that enter the standard library from Python 3.4 are still useful

Starting with Python 3.4, which will be released in March this year, enums will be added. (Documentation)

A backport has been developed that can be used from Python 2.4 and can be installed with pip install enum34.

Why you want an enum

Until now, for example, constants have been defined in this way.

class Spam:
    FOO = 1
    BAR = 2
    BAZ = 3

However, this method has the following problems.

There were ad hoc solutions and libraries, but I want this much standard library. Now that it's in the standard library, let's use it.

Simple usage

As with namedtuple, if you pass a model name first, then a list of names, or a space-separated string, the values will be assigned in order from 1.

The resulting type can access the element in three ways: attribute, call, and subscript.

>>> import enum
>>> Foo = enum.Enum("Foo", "foo bar baz")
>>> Foo.foo
<Foo.foo: 1>
>>> Foo(1)
<Foo.foo: 1>
>>> Foo["foo"]
<Foo.foo: 1>
>>> isinstance(Foo.foo, Foo)
True

The element has attributes named and value.

>>> Foo.foo.value
1
>>> Foo.foo.name
'foo'

The element itself is distinct from its value. Since all elements are singletons that are created at the same time as the class is created, you can compare them with ʻisinstead of==`.

>>> Foo.foo == 1
False
>>> Foo.foo is Foo(1) is Foo["foo"]
True
>>> Foo.foo is Foo(1)
True

You can also iterate.

>>> for m in Foo:
...     print(m)
...
Foo.foo
Foo.bar
Foo.baz

Use any value

By defining an inherited class instead of calling ʻEnum`, you can assign any value to an element instead of a sequential number from 1.

>>> class Bar(enum.Enum):
...     foo = 'x'
...     bar = 'y'
...     baz = 'z'
...
>>> Bar.foo
<Bar.foo: 'x'>

Limitations in Python 2

In Python 2, there is (basically) no way to take advantage of the order in which the elements of a class are defined. If you want to maintain the definition order when enumerating with the for statement, define the attribute __order__. This is an enum34 extension for Python 2 support, and the Python 3.4 enum module saves the definition order without any action.

>>> class Bar(enum.Enum):
...     __order__ = 'foo bar baz'
...     foo = 'x'
...     bar = 'y'
...     baz = 'z'
...
>>> list(Bar)
[<Bar.foo: 'x'>, <Bar.bar: 'y'>, <Bar.baz: 'z'>]

Treat elements as integers

Sometimes it's convenient for an element to act directly as an integer. In that case you can use IntEnum.

>>> class Foo(enum.IntEnum):
...     foo = 1
...     bar = 3
...     baz = 5
...
>>> Foo.foo == 1
True
>>> Foo.foo + 2
3
>>> Foo.foo < Foo.bar
True

In fact, this is the only definition of IntEnum.

class IntEnum(int, Enum):
    """Enum where members are also (and must be) ints"""

In this way, you can inherit the behavior you want the element to have.

>>> class Bar(str, enum.Enum):
...     foo = 'x'
...     bar = 'y'
...
>>> Bar.foo + Bar.bar
'xy'

While this is useful, dynamically typed Python runs the risk of confusing whether a variable has an Enum element or a pure value type. Let's use it with moderation.

about in

Even with IntEnum, I'm not sure if there is an Enum value that corresponds to that integer value using ʻin directly between the integer value and the Enum type. ʻIn can only be used on its Enum type element.

>>> class Foo(enum.IntEnum):
...     foo = 3
...     bar = 7
...
>>> 3 in Foo
False
>>> Foo.foo in Foo
True

You can do anything by inheriting EnumMeta and customizing it, or hitting the private API directly, but don't behave like that, just call it normally and check for exceptions.

If the call does not find a value, ValueError is thrown. You can use subscripts and KeyError in the same way when subtracting elements from a name.

>>> Foo(2)
ValueError: 2 is not a valid Foo
>>> Foo['xyzzy']
KeyError: 'xyzzy'

However, as a public API, a dictionary with the name as the key and the element as the value is prepared with the name __members__, so when looking up by name, you can also use ʻinor.get ()` or the usual dict operation. You can use it.

>>> Foo.__members__
{'foo': <Foo.foo: 3>, 'bar': <Foo.bar: 7>}
>>> Foo.__members__['foo']
<Foo.foo: 3>
>>> 'xyzzy' in Foo.__members__
False

Recommended Posts

Enum types that enter the standard library from Python 3.4 are still useful
Summary of versions of the Python standard library that are now server validated on https
Five useful Python data types that are easy to forget
(Python3) No. oO (Are you using the standard library?): 5 shaders
From Python 3.4, pip becomes the standard installer! ??
Have python parse the json entered from the standard input
I wanted to use the Python library from MATLAB
Find the part that is 575 from Wikipedia in Python
I tried using the Python library from Ruby with PyCall
What you can do with the Python standard library statistics
[Python] A program that finds the most common bird types
Pass OpenCV data from the original C ++ library to Python
From a book that programmers can learn (Python): Find the mode
Import modules that are often used when starting the python interpreter
Verification of the theory that "Python and Swift are quite similar"
Python standard module that can be used on the command line