Previously: In the test code, the close Angle bracket (>) indicates the command entered on the command line. A single line starting with the hash character (#) is output; The import of the library is only shown in the first test code of this article; the other code blocks omit the import of the library.

  • System type: Windows 10
  • Python version: Python 3.9.0

An enumeration is a collection of symbolic names (enumerators) that should be unique and immutable.

The enum module is divided into three parts. The first part mainly introduces enumeration features and enum classes. The second part introduces three variations and custom classes of enum classes. You can also view wechat articles through the portal.

portal

Enum — enumeration (1)

Enum — enumeration (2)

Enum — enumeration (3)

The first part

Create an Enum

Enumerations are created using class syntax, which makes them easy to read and write. There is, however, a way to create using a function. I’ll leave that for later, but I’ll focus on how to create using the class syntax.

import enum

class Test(enum.Enum) :
    A = 1
    B = 'b'
Copy the code

The above code creates an enumeration, and Class Test is an enumeration, where Test is the name of the enumeration object.

A = 1 and B = ‘B’ are called enumerators, and the ones to the left are called names of enumerators. Enumerators are usually used to represent constants, so enumerators are usually named in uppercase. The value to the right of the equals sign is called an enumerator. The value of an enumerator can be of any type, such as int, STR, etc., just note that the value of an enumerator is unique in the enumeration object.

Although the class syntax is used to create enumerations, enumerations are different from normal Python classes, and you can print some information for comparison:

"' the enumeration" '
class Test(enum.Enum) :
    A = 1

print(Test)                      # print enumeration
# <enum 'Test'>
print(Test.A)                    Print the enumerator itself
# Test.A
print(repr(Test.A))              Print the form of enumeration members in the interpreter
# <Test.A: 1>
print(type(Test.A))              Print the enumerator type
# <enum 'Test'>
print(isinstance(Test.A, Test))  Check whether the enumerator type is identical to the enumerator type
# True

Ordinary Python classes
class Common:
    A = 1

print(Common)                    # print class
# <class '__main__.Common'>
print(Common.A)                  Print the value of the class attribute
# 1
print(repr(Common.A))            Print the value of the class attribute in the interpreter
# 1
print(type(Common.A))            Print the type of the value of the class attribute
# <class 'int'>
print(isinstance(Common.A, Test))  Check whether the type of the value of the class attribute is the same as that of the class
# False
Copy the code

First, the object created by an enumeration is a type of its own, and the type name is the name of the enumeration (class name). An enumeration member (a property of the class) of an enumeration is created as an object, and the type is the type of the enumeration to which it belongs.

Enumerations can iterate in the order they are defined, and enumerators are immutable and hashable, so keys in dictionaries can use enumerators.

class Test(enum.Enum) :
    A = 1

test_dict = {}
for i in Test:
    test_dict[i] = 'value.' + str(i)
print(test_dict)
# {<Test.A: 1>: 'value.Test.A', <Test.B: 'b'>: 'value.Test.B', <Test.C: 'c'>: 'value.Test.C'}
print(test_dict[Test.A])
# value.Test.A
Copy the code

So how do you get enumerators? There are many ways to access enumerators:

class Test(enum.Enum) :
    A = 1

print(Test(1))       Programmatic access
# Test.A
print(Test['A'])     # entry access
# Test.A
print(Test.A.value)  The value property of the enumerator
# 1
print(Test.A.name)   The name attribute of the enumerator
# A
Copy the code

Enumerators are not allowed to have the same enumerator, but enumerators are allowed to have the same value. If multiple enumerators are searched for, the first one is returned in defined order.

Enumerator members have the same name
class Test(enum.Enum) :
    A = 1
    A = 2
# TypeError: Attempted to reuse key: 'A'

Enumeration members have the same value.
class Test(enum.Enum) :
    A = 1
    B = 1

print(Test(1))
# Test.A
Copy the code

The enum module defines a decorator, @enum. Unique, that can enforce that the value of an enumerator must also be unique.

@enum.unique
class Test(enum.Enum) :
    A = 1
    B = 1
# ValueError: duplicate values found in <enum 'Test'>: B -> A
Copy the code

The enum. Auto () method can be used instead of the input value in cases where the values of the enumerators are not important or where enumerators need not be specified individually.

class Test(enum.Enum) :
    A = enum.auto()
    B = enum.auto()
    C = 1

print(repr(Test.A))
# <Test.A: 1>
print(repr(Test.B))
# <Test.B: 2>
print(repr(Test.C))
# <Test.A: 1>
print(list(Test))
# [<Test.A: 1>, <Test.B: 2>]
Copy the code

When defining the value of an enumerator using enum.auto() and then using a value of type int again, you may end up with a different result than expected.

The return value of enum. Auto () is determined by the _generate_next_value_() function, which by default increments the value of the last enumerator of type int by one. This function can be overloaded, and when overloaded, the method definition must precede any member.

Define an enumerator of type int, then use the auto() function.
class Test(enum.Enum) :
    A = 2
    B = enum.auto()
    C = enum.auto()
print(list(Test))
# [<Test.A: 2>, <Test.B: 3>, <Test.C: 4>]

Define an enumerator whose value is not int, using the auto() function.
class Test(enum.Enum) :
    A = 'b'
    B = enum.auto()
    C = enum.auto()
print(list(Test))
# [<Test.A: 'b'>, <Test.B: 1>, <Test.C: 2>]

"' overloaded _generate_next_value_ ()" '
class Test(enum.Enum) :
    def _generate_next_value_(name, start, count, last_values) :
        return name  Return the name of the enumerator

    A = enum.auto()
    B = enum.auto()

print(list(Test))
# [<Test.A: 'A'>, <Test.B: 'B'>]
Copy the code

When we introduced enum. Auto (), in the test example, an enumerator was missing when the enumerator was converted to list because the missing enumerator had the same value as the previous enumerator. This enumerator is considered an alias and is not given when iterating over an enumerator.

class Test(enum.Enum) :
    A = 1
    B = 1
print(list(Test))
# [<Test.A: 1>]
Copy the code

The special attribute __members__ is a read-only ordered mapping from name to member. It contains all the enumerators in the enumeration, but at iteration time, because of aliases, the enumerator pointed to is the first one defined for that value.

class Test(enum.Enum) :
    A = 1
    B = 1
print(dict(Test.__members__))
# {'A': <Test.A: 1>, 'B': <Test.A: 1>}
Copy the code

Find all aliases in the enumeration:

class Test(enum.Enum) :
    A = 1
    B = 1
    C = 2
    D = 2
print([nameforName, enumeratorin Test.__members__.items() ifEnumerator. Name! Name =)# ['B', 'D']
Copy the code

Enumerations are a Python class that can have both ordinary and special methods. Here’s an example from the documentation:

class Mood(enum.Enum) :
    FUNKY = 1
    HAPPY = 3

    def describe(self) :
        return self.name, self.value

    def __str__(self) :
        return 'my custom str! {0} '.format(self.value)

    @classmethod
    def favorite_mood(cls) :
        return cls.HAPPY

print(Mood.favorite_mood())
# my custom str! 3
print(Mood.HAPPY.describe())
# ('HAPPY', 3)
print(str(Mood.FUNKY))
# my custom str! 1
Copy the code

Enumerations, however, have some limitations. Names starting and ending with a single underscore are reserved by enumerations and cannot be used. Exceptions include special method members (__str__(), __add__(), etc.), descriptors (methods are also descriptors), and variable names listed in _ignore_.

When we introduced the _generate_next_value_() function, we created an enumeration based on enum.Enum, which can be called enumeration A, to override the _generate_next_value_() function. An enumeration based on enumeration A is then created. Such a definition is allowed. Under the enum module, a new enum class must be based on one enum class, at most one entity data type, and any number of Object-based mixin classes as necessary. The order of these base classes is:

class EnumName([mix-in, ...,] [data-type,] base-enum):
    pass
Copy the code

In addition, enumerations that do not define enumerators can only be inherited by other enumerations, otherwise an error will be reported:

class A(enum.Enum) :
    AA = 1
class B(A) :
    BB = 2
# TypeError: B: cannot extend enumeration 'A'
Copy the code

The second part

IntEnum

The IntEnum class is a subclass of the Enum class and a subclass of int. The value of an IntEnum enumerator must be of type int. Any other type will cause an error.

class IntTest(enum.IntEnum) :
    A = 1
    B = 'b'
# ValueError: invalid literal for int() with base 10: 'b'
Copy the code

In addition, the IntEnum class differs from the Enum class in comparison. The IntEnum class compares with the Enum class in comparison.

Enumerators are equal only to themselves and aliases, enumerators are never equal to non-enumerator values, and enumerators can only be uncollated.

class TestA(enum.Enum) :
    A = 1
    B = 1
class TestB(enum.Enum) :
    A = 1
    B = 2

print(TestA.A is TestB.A)  # is comparison of enumerators with the same name and value from different enumerations
# False
print(TestA.A is TestA.B)  # Is comparison of same-valued enumerators of the same enumeration (alias)
# True
print(TestA.A == TestB.A)  == comparison of enumerators with the same name and value for different enumerations
# False
print(TestA.A == TestA.B)  # same-valued enumerators of the same enumeration == compare (aliases)
print(TestA.A == 1)  # compare with int
# False
print(TestA.A < TestA.B)  # sort comparison
# Traceback (most recent call last):
# File "e:\project\test\test.py", line 15, in 
      
# print(TestA.A < TestA.B)
# TypeError: '<' not supported between instances of 'TestA' and 'TestA'
Copy the code

IntEnum enumerators can be compared directly with values of type int, sorting comparisons are supported, and enumerators of the same name and value from different enumerations are equal when compared using ==.

class IntTestA(enum.IntEnum) :
    A = 1
    B = 1

class IntTestB(enum.IntEnum) :
    A = 1
    B = 2

print(IntTestA.A is IntTestB.A)  # is comparison of enumerators with the same name and value from different enumerations
# False
print(IntTestA.A is IntTestA.B)  # Is comparison of same-valued enumerators of the same enumeration (alias)
# True
print(IntTestA.A == IntTestB.A)  == comparison of enumerators with the same name and value for different enumerations
# True
print(IntTestA.A == IntTestA.B)  # same-valued enumerators of the same enumeration == compare (aliases)
# True
print(IntTestA.A == 1)  # compare with int
# True
print(IntTestA.A < IntTestB.B)  # sort comparison
# True
Copy the code

IntEnum otherwise behaves like int data:

class IntTestA(enum.IntEnum) :
    A = 1
    B = 2

print(['a'.'b'.'c'][IntTestA.A])  # enumerator as index
# b
print([i for i in range(IntTestA.B)])  Enumeration members as arguments to range()
# [0, 1]
Copy the code

Flag

Flag class is a subclass of an Enum, Flag and Enum class difference is Flag enumeration members can use the bitwise operators (&, |, ^ ~).

class TestFlag(enum.Flag) :
    A = 1
    B = 2
    C = A | B

print(list(TestFlag))  All enumerators
# [<TestFlag.A: 1>, <TestFlag.B: 2>, <TestFlag.C: 3>]

print(repr(TestFlag.A | TestFlag.B))  Enumerator A and B by bit or
# <TestFlag.C: 3>
print(repr(TestFlag.C))  Enumerator C
# <TestFlag.C: 3>
print(1 | 2)  # 1 and 2 are bitwise or
# 3
Copy the code

Enumerators of the Flag class can be defined with or without bitted operators, and if the resulting value is equal to that of the enumerator in the enumeration, the final result is an enumerator with the same value as the result. What if you use the bitbit operator when you use an enumerator, and you end up with a value equal to no enumerator in the enumeration?

class TestFlag(enum.Flag) :
    A = 1
    B = 2

print(repr(TestFlag.A | TestFlag.B))  Enumerator A and B by bit or
# <TestFlag.B|A: 3>
print(type(TestFlag.A | TestFlag.B))  Check the type of result
# <enum 'TestFlag'>
Copy the code

After using bit-based operators between enumerators of the Flag class, you get that enumerator if there is an enumerator with the same value in the enumeration, otherwise you get a new enumerator.

IntFlag

IntFlag is a subclass of Flag and Int. In fact, the IntFlag class is more like IntEnum, which can use bitwise operators. But in some ways IntFlag is unique.

First, IntFlag enumerators can only be numbers, as in the IntEnum class. IntFlag enumerators can be defined using bitbit operators, and they can be used with enumerators. The result is still an enumerator, as in the Flag class.

class TestIntFlag(enum.IntFlag) :
    A = 1
    B = 2
    C = A | B

print(repr(TestIntFlag.A | TestIntFlag.B))
# <TestIntFlag.C: 3>
Copy the code

Because it inherits Int, IntFlag enumerators can perform operations, index values, and so on, just like IntEnum enumerators. However, any operation performed by an enumerator of the IntFlag class other than bit-by-bit will disqualify an enumerator of the IntFlag class.

class TestIntFlag(enum.IntFlag) :
    A = 1
    B = 2

print(TestIntFlag.A + TestIntFlag.B)  # Addition
# 3
print(['a'.'b'.'c'.'d'][TestIntFlag.A])  # index values
# b
Copy the code

Custom class

When none of the above enumerated classes meet your requirements, you can modify the magic methods in the enumerated classes to change some features or functionality.

The __new__() magic method is called before each enumeration member definition to modify the enumeration member’s value and set custom properties.

Class Test(enum.Enum): A = 'aaa' B = 'BBB' def __new__(CLS, value): obj = object.__new__(cls) obj._value_ = str(value) + '... ' return obj print(list(Test)) # [<Test.A: 'aaa...'>, <Test.B: 'bbb...'>]Copy the code

The __init__() magic method is called after the enumeration member is defined, and performs essentially the same functionality as the __new__() magic method. The __init__() magic can define the value of an enumerator, but it is not allowed to use the __init__() magic to define the value of an enumerator.

Class Test(enum.Enum): A =' aaa' B =' BBB 'def __init__(self, pantone='unknown'): self._value_ = str(self.value) + '... ' print(list(Test)) # [<Test.A: 'aaa...'>, <Test.B: 'bbb...'>]Copy the code

Redefinition of the __repr__() magic method to modify what is returned when using the repr() function:

When using the repr() function, enumeration member values are not displayed.
class TestA(enum.Enum) :
    A = enum.auto()
    B = enum.auto()

    def __repr__(self) :
        return '<%s.%s>' % (self.__class__.__name__, self.name)


print(repr(TestA.A))
# <TestA.A>
print(TestA.A.value)  Get the value of the enumerator normally
# 1
Copy the code

With __repr__ () magic method similar to the __str__ (), __add__ (), __format__ (), __bool__ () and so on magic methods can be redefined.

Here are two interesting little examples from the official documentation:

An ordered enumeration that is not IntEnum based and thus preserves the normal Enum invariance.
class OrderedEnum(enum.Enum) :
    def __ge__(self, other) :
        if self.__class__ is other.__class__:
            return self.value >= other.value
        return NotImplemented

    def __gt__(self, other) :
        if self.__class__ is other.__class__:
            return self.value > other.value
        return NotImplemented

    def __le__(self, other) :
        if self.__class__ is other.__class__:
            return self.value <= other.value
        return NotImplemented

    def __lt__(self, other) :
        if self.__class__ is other.__class__:
            return self.value < other.value
        return NotImplemented

class Grade(OrderedEnum) :
    A = 5
    B = 4
    C = 3
    D = 2
    F = 1

print(Grade.C < Grade.A)
# True


An error is raised if duplicate member names are found instead of creating aliases.
class DuplicateFreeEnum(enum.Enum) :
    def __init__(self, *args) :
        cls = self.__class__
        if any(self.value == e.value for e in cls):
            a = self.name
            e = cls(self.value).name
            raise ValueError(
                "aliases not allowed in DuplicateFreeEnum: %r --> %r"
                % (a, e))

class Color(DuplicateFreeEnum) :
    RED = 1
    GREEN = 2
    BLUE = 3
    GRENE = 2
# ValueError: aliases not allowed in DuplicateFreeEnum: 'GRENE' --> 'GREEN'
Copy the code

The third part

The value of an unimportant enumerator

In some scenarios, people do not care what the value of an enumerator is, and you can define enumerations in the following ways:

  • useenum.auto()Function as the value of an enumerator
  • useobject()Value as an enumerator
  • Use descriptive characters as the value of the enumerator, while doing so to further interpret the enumerator
  • Use empty tuples as values, and in custom__new__()To redefine the value of an enumeration member (as long as it does not duplicate)

Having seen the above enumeration, this requirement is not limited to the above methods, as long as the value of the final enumerator does not repeat, such as a custom function that generates a random string.

import enum
import pickle  # sequestration


'''enum.auto()'''
class TestA(enum.Enum) :
    A = enum.auto()
    B = enum.auto()

'''object()'''
class TestB(enum.Enum) :
    A = object()
    B = object(a)"Description string"
class TestC(enum.Enum) :
    A = 'letters A'
    B = 'the letter B'

Tuples with a custom __new__()"
class TestD(enum.Enum) :
    A = ()
    B = ()

    def __new__(cls, *args) :
        Gets the current enumerator number and increments it, assigning the result to the newly defined enumerator.
        value = len(cls.__members__) + 1
        obj = object.__new__(cls)  You must create an enumerator object
        obj._value_ = value
        return obj  You must return an enumerator object

print(list(TestA))
# [<TestA.A: 1>, <TestA.B: 2>]
print(list(TestB))
# [<TestB.A: <object object at 0x0000023986178210>>, <TestB.B: <object object at 0x0000023986178220>>]
print(list(TestC))
# [
      
       , 
       
        ]
       '>
      
print(list(TestD))
# [<TestD.A: 1>, <TestD.B: 2>]
Copy the code

sequestration

Enumerations can be sealed and unsealed:

class Test(enum.Enum): A = enum. Auto () B = enum. Auto () Stored enumerators A = pickle.dumps(test.a) Unloaded enumerators A = pickle.loads(stored enumerators A) print(stored enumerators A) # b'\x80\x04\x95\x1b\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x04Test\x94\x93\x94K\x01\x85\x94R\x94.' Print (test. A print(test. A is the enumerator A) # TrueCopy the code

Functional API

Enum classes are callable objects, that is, Enum classes can be called like functions:

Enum(value='NewEnumName', names=<... >, *, module='... ', qualname='... ', type=<mixed-in class>, start=1) parameter: value: STR, enumeration names: names of enumerators. You can set the enumeration member module in various ways: Qualname: qualName: STR, qualname: STR, qualname: STR, qualname: STR, qualname: STR, qualname: STR, qualname: STR, qualname: STR, qualname: STR, qualname: STR, qualname: STR, qualname: Keyword argument, int, starting value of enumeration member. Default is 1. Return value: enumerationCopy the code

The names argument can be passed in a string separated by Spaces or commas. The delimited substrings are the names of the enumerators, whose values are incremented sequentially, starting with the start argument, which defaults to 1. The NAMES argument can also be passed in iterators or maps.

"" separated by a space.
TestA = enum.Enum('TestEnum'.'A B C D')
print(list(TestA))
# [<TestEnum.A: 1>, <TestEnum.B: 2>, <TestEnum.C: 3>, <TestEnum.D: 4>]

"" separated by Spaces, set to start at 10" "
TestB = enum.Enum('TestEnum'.'A B C D', start=10)
print(list(TestB))
# [<TestEnum.A: 10>, <TestEnum.B: 11>, <TestEnum.C: 12>, <TestEnum.D: 13>]

Passing in the list sets the enumerators, whose values are incremented in order, starting with the start argument.
TestC = enum.Enum('TestEnum'['A'.'B'.'C'.'D'])
print(list(TestC))
# [<TestEnum.A: 1>, <TestEnum.B: 2>, <TestEnum.C: 3>, <TestEnum.D: 4>]

Pass in a list of elements in the format (' name ', 'value ').
TestD = enum.Enum('TestEnum', [('A'.11), ('B'.22), ('C'.33), ('D'.44)])
print(list(TestD))
# [<TestEnum.A: 11>, <TestEnum.B: 22>, <TestEnum.C: 33>, <TestEnum.D: 44>]

"" incoming mapping in the format {name: value}" "
TestE = enum.Enum('TestEnum', {'A': 11.'B': 22.'C': 33.'D': 44})
print(list(TestE))
# [<TestEnum.A: 11>, <TestEnum.B: 22>, <TestEnum.C: 33>, <TestEnum.D: 44>]
Copy the code

The module parameter needs to be passed in a module in order to be executed successfully when the sequestration operation takes place. If you specify an incorrect module, the seal operation will report an error. If the parameter value is set to None or __name__, the final module pointing to the current file __main__.

The module argument defaults to None.
TestA = enum.Enum('TestA'.'AAA BBB CCC')
print(pickle.loads(pickle.dumps(TestA)))  # Seal + unseal
# <enum 'TestA'>

Module argument set to __name__ ""
TestB = enum.Enum('TestB'.'AAA BBB CCC', module=__name__)
print(pickle.loads(pickle.dumps(TestB)))
# <enum 'TestB'>

The "module" argument is set to a non-existent module.
TestD = enum.Enum('TestD'.'AAA BBB CCC', module='aaa')
print(pickle.loads(pickle.dumps(TestD)))
# _pickle.PicklingError: Can't pickle <enum 'TestD'>: import of module 'aaa' failed
Copy the code

The qualname parameter specifies a specific location in the module. Like the module parameter, the qualname parameter is set to seal operations, and pickle protocol version 4 depends on the location in some cases.

Boolean value

The Boolean value of an enumeration class is always True, and the Boolean value of an enumeration member in an enumeration is always True. The Boolean value of an enumerator is independent of the enumerator’s value. If you want to calculate a Boolean value from the enumerator’s value, you can modify the __bool__() magic method.

Does not modify enumeration classes for __bool__() magic methods.
class Test(enum.Enum) :
    A = 0
    B = None

print(bool(Test))
# True
print(bool(Test.A))
# True
print(bool(Test.B))
# True

Modify the enumeration class of the __bool__() magic method.
class Test(enum.Enum) :
    A = 0

print(bool(Test.A))
# False
Copy the code

Support for enumerating properties used by classes

__members__ is a read-only ordered mapping of member_name:member entry. The result is a dictionary with the structure {name: enumerator object}.

class Test(enum.Enum) :
    A = 1
    B = 2
    C = 1

print(Test.__members__)
# {'A': <Test.A: 1>, 'B': <Test.B: 2>, 'C': <Test.A: 1>}
Copy the code

Supports attributes used by enumerators

  • _name__value_Return the name and value of the enumerator, respectively;
  • _generate_next_value_Is used in functional apis to get appropriate values for enumerators, and can also be printed;
  • _missing_A lookup function used when a value is not found;
  • _ignore_Is a list of names, and the result can belistOr it could bestr, will not be converted to enumerators and will be removed from the final class;
  • _order_Python2Legacy properties, inPython2Is responsible for recording the order in which enumerators are defined.
Printable properties
class Test(enum.Enum) :
    A = 1
    B = 2
    C = 1

print(Test.A._name_)
# 'A'
print(Test.A._value_)
# 1
print(Test.A._generate_next_value_)
# <bound method Enum._generate_next_value_ of <Test.A: 1>>

Usage of "_ignore_"
class Period(enum.Enum) :
    _ignore_ = 'Period i'
    Period = vars(a)for i in range(3):
        Period['obj_%d' % i] = i

print(list(Period))
# [<Period.obj_0: 0>, <Period.obj_1: 1>, <Period.obj_2: 2>]
Copy the code

Public account: python Grocery store, specializing in Python language and related knowledge. Discover more original articles, looking forward to your attention.

The resources

Official documentation: docs.python.org/zh-cn/3/lib…

Source code: Lib/enum. Py