Псевдонимы перечислений в Python и @enum.unique

В этом руководстве вы узнаете о псевдонимах элементов перечисления в Python и о том, как использовать декоратор enum unique для обеспечения уникальности значений элементов.

Содержание

Что такое псевдонимы элементов перечислений?

По определению значения членов перечисления уникальны. Однако вы можете создавать разные имена элементов с одинаковыми значениями.

Например, следующий код определяет перечисление Color:

from enum import Enum


class Color(Enum):
    RED = 1
    CRIMSON = 1
    SALMON = 1
    GREEN = 2
    BLUE = 3

В этом примере перечисление Color содержит элементы RED, CRIMSON и SALMON с одинаковым значением 1. Когда вы определяете несколько членов перечисления с одинаковыми значениями, Python создает не разные члены, а псевдонимы. RED является основным элементом, а CRIMSON и SALMON являются псевдонимами члена RED.

Следующие операторы возвращают значение True, поскольку CRIMSON и SALMON являются членами RED:

print(Color.RED is Color.CRIMSON)
print(Color.RED is Color.SALMON)

Выход:

True
True

Когда вы ищете элемент по значению, вы всегда получаете основной элемент, а не псевдонимы. Например, следующий оператор возвращает элемент RED:

print(Color(1))

Выход:

Color.RED

При переборе перечисления с псевдонимами вы получите только основные члены, а не псевдонимы. Пример:

for color in Color:
    print(color)

Он возвращает только три члена:

Color.RED
Color.GREEN
Color.BLUE

Чтобы получить все члены, включая псевдонимы, вам нужно использовать свойство __member__ класса перечисления. Например:

from enum import Enum
from pprint import pprint


class Color(Enum):
    RED = 1
    CRIMSON = 1
    SALMON = 1
    GREEN = 2
    BLUE = 3


pprint(Color.__members__)

Выход:

mappingproxy({'BLUE': <Color.BLUE: 3>,
              'CRIMSON': <Color.RED: 1>,
              'GREEN': <Color.GREEN: 2>,
              'RED': <Color.RED: 1>,
              'SALMON': <Color.RED: 1>})

Как ясно видно из выходных данных, CRIMSON и SALMON ссылаются на один и тот же объект, на который ссылается член RED:

<Color.RED: 1>

Когда использовать псевдонимы перечислений

Псевдонимы перечисления могут быть полезны в некоторых ситуациях. Например, предположим, что вам приходится иметь дело с API двух разных систем. Каждая система имеет свой статус ответа, имеющий то же значение, что показано в следующей таблице:

Система 1 Система 2 Значение
REQUESTING PENDING Запрос находится в обработке
OK FULFILLED Запрос успешно выполнен
NOT_OK REJECTED Запрос не выполнен

Чтобы стандартизировать коды состояния этих систем, вы можете использовать псевдонимы перечисления следующим образом:

Ваша система Система 1 Система 2 Значение
IN_PROGRESS REQUESTING PENDING Запрос находится в обработке
SUCCESS OK FULFILLED Запрос успешно выполнен
ERROR NOT_OK REJECTED Запрос не выполнен

Следующий код определяет перечисление ResponseStatus с псевдонимами:

from enum import Enum


class ResponseStatus(Enum):
    # in progress
    IN_PROGRESS = 1
    REQUESTING = 1
    PENDING = 1

    # success
    SUCCESS = 2
    OK = 2
    FULFILLED = 2

    # error
    ERROR = 3
    NOT_OK = 3
    REJECTED = 3

Ниже сравнивается код ответа системы 1, чтобы проверить, был ли запрос успешным или нет:

code = 'OK'
if ResponseStatus[code] is ResponseStatus.SUCCESS:
    print('The request completed successfully')

Выход:

The request completed successfully

Аналогичным образом вы можете проверить код ответа от системы 2, чтобы убедиться, что запрос был успешным:

code = 'FULFILLED'
if ResponseStatus[code] is ResponseStatus.SUCCESS:
    print('The request completed successfully')

Выход:

print('The request completed successfully')

Декоратор @enum.unique

Чтобы определить перечисление без псевдонимов, вы можете осторожно использовать уникальные значения для его членов. Например:

from enum import Enum


class Day(Enum):
    MON = 'Monday'
    TUE = 'Tuesday'
    WED = 'Wednesday'
    THU = 'Thursday'
    FRI = 'Friday'
    SAT = 'Saturday'
    SUN = 'Sunday'

Но вы можете случайно использовать одни и те же значения для двух членов, например:

class Day(Enum):
    MON = 'Monday'
    TUE = 'Monday'
    WED = 'Wednesday'
    THU = 'Thursday'
    FRI = 'Friday'
    SAT = 'Saturday'
    SUN = 'Sunday'

В этом примере TUE является псевдонимом члена MON, чего вы, возможно, не ожидаете. Чтобы гарантировать, что перечисление не имеет псевдонима, вы можете использовать декоратор @enum.unique из модуля enum.

Когда вы используете декоратор @enum.unique, Python выдает исключение, если перечисление имеет псевдонимы.

Например, следующее вызовет ValueError:

import enum

from enum import Enum


@enum.unique
class Day(Enum):
    MON = 'Monday'
    TUE = 'Monday'
    WED = 'Wednesday'
    THU = 'Thursday'
    FRI = 'Friday'
    SAT = 'Saturday'
    SUN = 'Sunday'

Ошибка:

ValueError: duplicate values found in <enum 'Day'>: TUE -> MON
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *