Python

Enum HOWTO

Enum`은 고유한 값에 바인딩된 심볼릭 이름의 집합입니다. 이는 전역 변수와 유사하지만, 유용한 :func:`repr, 그룹화, 타입 안정성 및 기타 몇 가지 기능을 제공합니다.

이들은 제한된 선택지 중 하나를 가질 수 있는 변수가 있을 때 가장 유용합니다. 예를 들어, 요일 등이 있습니다:

>>> from enum import Enum
>>> class Weekday(Enum):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 3
...     THURSDAY = 4
...     FRIDAY = 5
...     SATURDAY = 6
...     SUNDAY = 7

또는 RGB 기본 색상입니다:

>>> from enum import Enum
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3

보시다시피, :class:`Enum`을 생성하는 것은 자체적으로 :class:`Enum`을 상속받는 클래스를 작성하는 것만큼 간단합니다.

참고

열거형 멤버의 경우

열거형은 상수를 나타내는 데 사용되므로, 그리고 믹스인 클래스 메서드/속성과 열거형 이름 간의 이름 충돌 문제를 피하는 데 도움이 되므로, 멤버에 대해 대문자(UPPER_CASE) 이름을 사용하는 것을 강력히 권장하며, 예제에서도 이 스타일을 사용할 것입니다.

열거형의 성격에 따라 멤버의 값이 중요할 수도 있고 아닐 수도 있지만, 어느 쪽이든 그 값은 해당 멤버를 얻는 데 사용될 수 있습니다:

>>> Weekday(3)
<Weekday.WEDNESDAY: 3>

보시다시피, 멤버의 repr() 은 열거형 이름, 멤버 이름, 그리고 값을 보여줍니다. 멤버의 str() 은 오직 열거형 이름과 멤버 이름만을 보여줍니다:

>>> print(Weekday.THURSDAY)
Weekday.THURSDAY

열거형 멤버의 는 그것이 속한 열거형입니다:

>>> type(Weekday.MONDAY)
<enum 'Weekday'>
>>> isinstance(Weekday.FRIDAY, Weekday)
True

열거형 멤버는 :attr:`!name`만을 포함하는 속성을 가집니다:

>>> print(Weekday.TUESDAY.name)
TUESDAY

마찬가지로, 해당 멤버는 :attr:`!value`에 대한 속성을 가집니다:

>>> Weekday.WEDNESDAY.value
3

열거가 타입을 단지 이름/값 쌍으로 다루는 많은 언어와 달리, Python Enums에는 추가적인 동작을 부여할 수 있습니다. 예를 들어, datetime.date`는 요일을 반환하는 개의 메서드인 :meth:`~datetime.date.weekday`와 :meth:`~datetime.date.isoweekday`를 가지고 있습니다. 차이점은 하나는 0에서 6까지 계산하고 다른 하나는 1에서 7까지 계산한다는 것입니다. 차이를 직접 추적하는 대신, :class:!Weekday` 열거형에 메서드를 추가하여 date 인스턴스에서 요일을 추출하고 일치하는 열거형 멤버를 반환할 수 있습니다:

@classmethod
def from_date(cls, date):
    return cls(date.isoweekday())

완전한 Weekday 열거형은 다음과 같이 보입니다:

>>> class Weekday(Enum):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 3
...     THURSDAY = 4
...     FRIDAY = 5
...     SATURDAY = 6
...     SUNDAY = 7
...     #
...     @classmethod
...     def from_date(cls, date):
...         return cls(date.isoweekday())

이제 오늘이 무엇인지 알아낼 수 있습니다! 다음을 관찰하십시오:

>>> import datetime as dt
>>> Weekday.from_date(dt.date.today())
<Weekday.TUESDAY: 2>

물론, 다른 날짜에 이것을 읽고 있다면 그날짜가 보이게 될 것입니다.

Weekday 열거형은 변수에 하루만 필요할 경우 유용하지만, 여러 날이 필요한 경우는 어떨까요? 주 동안의 일을 플롯하는 함수를 작성하는데 :class:`list`를 사용하고 싶지 않다면, 다른 유형의 :class:`Enum`을 사용할 수 있습니다:

>>> from enum import Flag
>>> class Weekday(Flag):
...     MONDAY = 1
...     TUESDAY = 2
...     WEDNESDAY = 4
...     THURSDAY = 8
...     FRIDAY = 16
...     SATURDAY = 32
...     SUNDAY = 64

두 가지를 변경했습니다. :class:`Flag`로부터 상속받았고, 값들은 모두 2의 거듭제곱입니다.

위의 원래 Weekday 열거형과 마찬가지로, 단일 선택을 가질 수 있습니다:

>>> first_week_day = Weekday.MONDAY
>>> first_week_day
<Weekday.MONDAY: 1>

하지만 :class:`Flag`는 여러 멤버를 하나의 변수에 결합할 수도 있게 해줍니다:

>>> weekend = Weekday.SATURDAY | Weekday.SUNDAY
>>> weekend
<Weekday.SATURDAY|SUNDAY: 96>

심지어 Flag 변수를 반복(iterate)할 수도 있습니다:

>>> for day in weekend:
...     print(day)
Weekday.SATURDAY
Weekday.SUNDAY

좋습니다. 이제 할 일들을 설정해 봅시다:

>>> chores_for_ethan = {
...     'feed the cat': Weekday.MONDAY | Weekday.WEDNESDAY | Weekday.FRIDAY,
...     'do the dishes': Weekday.TUESDAY | Weekday.THURSDAY,
...     'answer SO questions': Weekday.SATURDAY,
...     }

그리고 주어진 날짜의 할 일을 표시할 함수를 작성합니다:

>>> def show_chores(chores, day):
...     for chore, days in chores.items():
...         if day in days:
...             print(chore)
...
>>> show_chores(chores_for_ethan, Weekday.SATURDAY)
answer SO questions

멤버의 실제 값이 중요하지 않은 경우에는, :func:`auto`를 값으로 사용하여 수고를 덜 수 있습니다:

>>> from enum import auto
>>> class Weekday(Flag):
...     MONDAY = auto()
...     TUESDAY = auto()
...     WEDNESDAY = auto()
...     THURSDAY = auto()
...     FRIDAY = auto()
...     SATURDAY = auto()
...     SUNDAY = auto()
...     WEEKEND = SATURDAY | SUNDAY

열거형 멤버 및 그들의 어트리뷰트에 프로그래밍 방식 접근

때로는 열거형 멤버에 프로그램 방식으로 액세스하는 것이 유용합니다 (즉, Color.RED 를 사용할 수 없는 경우처럼 컴파일 시점에 정확한 색상을 알 수 없을 때). Enum 은 이러한 접근을 허용합니다:

>>> Color(1)
<Color.RED: 1>
>>> Color(3)
<Color.BLUE: 3>

name 으로 열거형 멤버에 액세스하려면, 항목 접근(item access)을 사용하십시오:

>>> Color['RED']
<Color.RED: 1>
>>> Color['GREEN']
<Color.GREEN: 2>

열거형 멤버가 있고 그 name 또는 :attr:`!value`가 필요하면:

>>> member = Color.RED
>>> member.name
'RED'
>>> member.value
1

열거형 멤버 및 값 복제

이름이 같은 열거형 멤버가 두 개 있는 것은 유효하지 않습니다:

>>> class Shape(Enum):
...     SQUARE = 2
...     SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: 'SQUARE' already defined as 2

그러나, 열거형 멤버는 다른 이름들을 가질 수 있습니다. 값(value)이 같은 두 항목 AB 가 주어지고 (그리고 A 가 먼저 정의되고), B 는 멤버 A 의 별칭입니다. A 의 값을 통한 조회는 멤버 A 를 반환합니다. A 의 이름으로 인한 조회 또한 멤버 A 를 반환합니다. B 의 이름으로 인한 조회도 멤버 A 를 반환합니다:

>>> class Shape(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>

참고

이미 정의된 어트리뷰트(또 다른 멤버, 메서드 등)와 같은 이름을 가진 멤버를 생성하거나, 멤버와 같은 이름의 어트리뷰트를 생성하려고 하는 것은 허용되지 않습니다.

열거형 값을 고유하게 유지하기

기본적으로 열거형은 여러 이름을 같은 값의 별칭으로 허용합니다. 이 동작을 원하지 않는 경우, @unique 데코레이터를 사용할 수 있습니다:

>>> from enum import Enum, unique
>>> @unique
... class Mistake(Enum):
...     ONE = 1
...     TWO = 2
...     THREE = 3
...     FOUR = 3
...
Traceback (most recent call last):
...
ValueError: <enum 'Mistake'>에서 중복 값을 찾았습니다: FOUR -> THREE

자동 값 사용하기

정확한 값이 중요하지 않은 경우, :class:`auto`를 사용할 수 있습니다:

>>> from enum import Enum, auto
>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> [member.value for member in Color]
[1, 2, 3]

값은 :func:`~Enum._generate_next_value_`에 의해 선택되며, 이를 재정의할 수 있습니다:

>>> class AutoName(Enum):
...     @staticmethod
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> [member.value for member in Ordinal]
['NORTH', 'SOUTH', 'EAST', 'WEST']

참고

_generate_next_value_() 메서드는 모든 멤버보다 먼저 정의되어야 합니다.

반복(Iteration)

열거형의 멤버를 순회할 때는 별칭이 제공되지 않습니다:

>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]
>>> list(Weekday)
[<Weekday.MONDAY: 1>, <Weekday.TUESDAY: 2>, <Weekday.WEDNESDAY: 4>, <Weekday.THURSDAY: 8>, <Weekday.FRIDAY: 16>, <Weekday.SATURDAY: 32>, <Weekday.SUNDAY: 64>]

별칭인 Shape.ALIAS_FOR_SQUAREWeekday.WEEKEND 는 표시되지 않는 점에 유의하십시오.

특별 어트리뷰트 __members__ 은 이름에서 멤버로의 읽기 전용 순서 있는 매핑입니다. 열거형에 정의된 모든 별칭을 포함하여, 모든 이름을 포함합니다:

>>> for name, member in Shape.__members__.items():
...     name, member
...
('SQUARE', <Shape.SQUARE: 2>)
('DIAMOND', <Shape.DIAMOND: 1>)
('CIRCLE', <Shape.CIRCLE: 3>)
('ALIAS_FOR_SQUARE', <Shape.SQUARE: 2>)

__members__ 어트리뷰트는 열거형 멤버에 대한 상세한 프로그래밍 방식의 액세스에 사용될 수 있습니다. 예를 들어, 모든 별칭을 찾는 방법입니다:

>>> [name for name, member in Shape.__members__.items() if member.name != name]
['ALIAS_FOR_SQUARE']

참고

플래그의 별칭에는 여러 플래그가 설정된 값(예: 3)과 플래그가 설정되지 않은 값(즉, 0)이 포함됩니다:

비교(Comparisons)

열거형 멤버는 아이덴티티를 기준으로 비교됩니다:

>>> Color.RED is Color.RED
True
>>> Color.RED is Color.BLUE
False
>>> Color.RED is not Color.BLUE
True

두 열거형 값 간의 순서 있는 비교는 지원되지 않습니다. Enum 멤버는 정수가 아닙니다(하지만 아래의 IntEnum 을 참조하십시오):

>>> Color.RED < Color.BLUE
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'Color' and 'Color'

동등성 비교는 정의되어 있습니다:

>>> Color.BLUE == Color.RED
False
>>> Color.BLUE != Color.RED
True
>>> Color.BLUE == Color.BLUE
True

비열거형 값과의 동등성 비교는 항상 False 를 반환합니다(다시, IntEnum 은 다르게 동작하도록 명시적으로 설계되었으므로, 아래를 참조하십시오):

>>> Color.BLUE == 2
False

경고

모듈을 다시 로드할 수 있습니다. 만약 다시 로드된 모듈이 열거형을 포함한다면, 해당 열거형들은 재생성될 것이며 새 멤버는 원본 멤버와 동일하거나 같다고 비교되지 않을 수 있습니다.

열거형의 허용되는 멤버 및 속성

대부분의 위의 예제는 열거형 값에 정수를 사용합니다. 정수 사용은 간편하고 편리하지만 (`함수형 API <Functional API>`에서 기본적으로 제공됩니다) 엄격하게 강제되지는 않습니다. 대부분의 사용 사례에서, 열거의 실제 값이 무엇인지는 중요하지 않습니다. 하지만 그 값이 중요하다면, 열거형은 임의의 값을 가질 수 있습니다.

열거형은 Python 클래스이며, 평소와 같이 메서드와 특수 메서드를 가질 수 있습니다. 만약 다음과 같은 열거형이 있다고 가정해 봅시다:

>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # self는 여기에 멤버입니다
...         return self.name, self.value
...
...     def __str__(self):
...         return '내 커스텀 str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # cls는 여기에 열거형입니다
...         return cls.HAPPY
...

그런 다음:

>>> Mood.favorite_mood()
<Mood.HAPPY: 3>
>>> Mood.HAPPY.describe()
('HAPPY', 3)
>>> str(Mood.FUNKY)
'my custom str! 1'

허용되는 규칙은 다음과 같습니다: 단일 밑줄로 시작하고 끝나는 이름은 enum에 의해 예약되어 사용할 수 없습니다. 열거형 내에 정의된 다른 모든 어트리뷰트는 특수 메서드 (__str__(), __add__() 등), 디스크립터 (메서드도 디스크립터입니다) 및 :attr:`~Enum._ignore_`에 나열된 변수 이름을 제외하고 이 열거의 멤버가 됩니다:

참고: 만약 열거형이 __new__() 및/또는 __init__() 를 정의한다면, 열거형 멤버에 제공된 모든 값은 해당 메서드로 전달됩니다. 예시는 Planet 을 참조하십시오.

참고

__new__() 메서드는 정의된 경우 Enum 멤버 생성 중에 사용되며, 이후 클래스 생성 후 기존 멤버를 조회하는 데 사용되는 Enum의 :meth:`~object.__new__`로 대체됩니다. 자세한 내용은 :ref:`new-vs-init`을 참조하십시오.

제한된 열거형 서브클래싱

새로운 Enum 클래스는 하나의 기본 Enum 클래스, 최대 하나의 구상 데이터형 및 필요한 만큼의 object 기반 믹스인 클래스를 가져야 합니다. 이 베이스 클래스의 순서는 다음과 같습니다:

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

또한, 열거형이 아무 멤버도 정의하지 않은 경우에만 서브클래싱하는 것이 허용됩니다. 따라서 이것은 금지됩니다:

>>> class MoreColor(Color):
...     PINK = 17
...
Traceback (most recent call last):
...
TypeError: <enum 'MoreColor'> cannot extend <enum 'Color'>

하지만 이것은 허용됩니다:

>>> class Foo(Enum):
...     def some_behavior(self):
...         pass
...
>>> class Bar(Foo):
...     HAPPY = 1
...     SAD = 2
...

멤버를 정의하는 enum을 서브 클래싱하면 타입과 인스턴스의 일부 중요한 불변성(invariants)이 위반될 수 있습니다. 반면, 열거형 그룹 간에 공통 동작을 공유하도록 허용하는 것은 의미가 있습니다. (예: OrderedEnum 을 참조하십시오.)

Dataclass 지원

:class:`~dataclasses.dataclass`에서 상속받을 때, :meth:`~Enum.__repr__`는 상속된 클래스 이름(name)을 생략합니다. 예를 들면 다음과 같습니다:

>>> from dataclasses import dataclass, field
>>> @dataclass
... class CreatureDataMixin:
...     size: str
...     legs: int
...     tail: bool = field(repr=False, default=True)
...
>>> class Creature(CreatureDataMixin, Enum):
...     BEETLE = 'small', 6
...     DOG = 'medium', 4
...
>>> Creature.DOG
<Creature.DOG: size='medium', legs=4>

표준 repr() 을 사용하려면 dataclass() 인자를 repr=False 로 사용하십시오.

버전 3.12에서 변경: 값 영역에는 dataclass 필드만 표시되며, dataclass 이름은 표시되지 않습니다.

참고

@~dataclasses.dataclass 데코레이터를 Enum 및 그 서브클래스에 추가하는 것은 지원하지 않습니다. 오류를 발생시키진 않지만, 멤버들이 서로 동등하다는 등 런타임에 매우 이상한 결과를 초래합니다:

>>> @dataclass               # 이렇게 하지 마세요: 의미가 없습니다.
... class Color(Enum):
...    RED = 1
...    BLUE = 2
...
>>> Color.RED is Color.BLUE
False
>>> Color.RED == Color.BLUE  # 문제는 여기에 있습니다: 동등해서는 안 됩니다.
True

피클링

열거형은 피클하고 역 피클 할 수 있습니다:

>>> from test.test_enum import Fruit
>>> from pickle import dumps, loads
>>> Fruit.TOMATO is loads(dumps(Fruit.TOMATO))
True

일반적인 제한 사항이 적용됩니다: 피클 가능한 열거형은 열거형을 임포트 해야 하므로, 모듈의 최상위 수준에서 정의되어야 합니다.

참고

피클 프로토콜 버전 4를 사용하면 다른 클래스에 중첩된 enum도 쉽게 피클할 수 있습니다.

열거형 클래스 내에서 __reduce_ex__() 정의를 통해 열거형 멤버가 어떻게 피클되거나 역 피클되는지 수정할 수 있습니다. 기본 메서드는 값 기준 방식(by-value)이지만, 복잡한 값을 가진 enum은 이름 기준 방식(by-name)을 사용하려고 할 수 있습니다:

>>> import enum
>>> class MyEnum(enum.Enum):
...     __reduce_ex__ = enum.pickle_by_enum_name

참고

플래그에 대해 이름 기반 방식을 사용하는 것은 권장되지 않습니다. 이름 없는 별칭은 역 피클 되지 않기 때문입니다.

함수형 API

Enum 클래스는 호출 가능하며, 다음의 함수형 API를 제공합니다:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> list(Animal)
[<Animal.ANT: 1>, <Animal.BEE: 2>, <Animal.CAT: 3>, <Animal.DOG: 4>]

이 API의 의미론은 :class:`~collections.namedtuple`과 유사합니다. :class:`Enum`을 호출하는 첫 번째 인자는 열거형의 이름을 나타냅니다.

두 번째 인자는 열거형 멤버 이름의 소스 입니다. 공백으로 구분된 이름의 문자열, 이름의 시퀀스, 키/값 쌍 2-튜플의 시퀀스 또는 이름과 값의 매핑(예: 딕셔너리)일 수 있습니다. 마지막 두 옵션은 열거형에 임의의 값을 할당할 수 있게 하며; 나머지들은 1부터 시작하여 증가하는 정수를 자동으로 할당합니다 (다른 시작 값을 지정하려면 start 매개 변수를 사용하십시오). Enum 에서 파생된 새 클래스가 반환됩니다. 즉, 위의 Animal 에 대한 대입은 다음과 동등합니다:

>>> class Animal(Enum):
...     ANT = 1
...     BEE = 2
...     CAT = 3
...     DOG = 4
...

시작 번호로 0 이 아니라 1 을 기본값으로 설정하는 이유는 0 이 부울 의미상(boolean sense) False 이지만, 기본적으로 열거형 멤버들은 모두 True 로 평가되기 때문입니다.

함수형 API로 생성된 열거형을 피클하는 것은 까다로울 수 있습니다. 왜냐하면 프레임 스택의 구현 세부 사항을 사용하여 해당 열거형이 어느 모듈에서 생성되고 있는지 파악하려 하기 때문입니다 (예: 별도의 모듈에 있는 유틸리티 함수를 사용하면 실패할 것이고, IronPython이나 Jython에서는 작동하지 않을 수도 있습니다). 해결책은 다음과 같이 모듈 이름을 명시적으로 지정하는 것입니다:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', module=__name__)

경고

module 이 제공되지 않고, Enum이 무엇인지 판단할 수 없는 경우, 새 Enum 멤버는 역 피클 할 수 없습니다. 오류를 소스에 가깝게 유지하려면, 피클링이 비활성화됩니다.

새로운 pickle 프로토콜 4도 일부 상황에서 __qualname__\가 pickle이 클래스를 찾을 수 있는 위치로 설정되는 것에 의존합니다. 예를 들어, 클래스가 전역 스코프의 SomeData 클래스 내에 사용 가능했던 경우:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG', qualname='SomeData.Animal')

완전 서명은 다음과 같습니다:

Enum(
    value='NewEnumName',
    names=<...>,
    *,
    module='...',
    qualname='...',
    type=<mixed-in class>,
    start=1,
    )
  • value: 새 enum 클래스가 이름으로 기록할 값입니다.

  • names: 열거형 멤버들입니다. 이는 공백 또는 쉼표로 구분된 문자열일 수 있으며 (지정하지 않는 한 값은 1부터 시작합니다):

    'RED GREEN BLUE' | 'RED,GREEN,BLUE' | 'RED, GREEN, BLUE'
    

    또는 이름의 이터레이터:

    ['RED', 'GREEN', 'BLUE']
    

    또는 (이름, 값) 쌍의 이터레이터:

    [('CYAN', 4), ('MAGENTA', 5), ('YELLOW', 6)]
    

    또는 매핑:

    {'CHARTREUSE': 7, 'SEA_GREEN': 11, 'ROSEMARY': 42}
    
  • 모듈 이름: 새 열거형 클래스를 찾을 수 있는 모듈의 이름입니다.

  • qualname: 모듈에서 새로운 열거형 클래스를 찾을 수 있는 곳.

  • type: 새로운 열거형 클래스와 혼합할 형.

  • start: 이름만 전달될 때 세기 시작할 숫자.

버전 3.5에서 변경: start 매개 변수는 키워드 인자로 지정할 수 있습니다.

파생 열거형

IntEnum️

제공되는 첫 번째 :class:`Enum`의 변형은 :class:`int`의 서브 클래스이기도 합니다. :class:`IntEnum`의 멤버는 정수와 비교할 수 있으며, 확장하여 서로 다른 유형의 정수 열거형도 비교할 수 있습니다:

>>> from enum import IntEnum
>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Request(IntEnum):
...     POST = 1
...     GET = 2
...
>>> Shape == 1
False
>>> Shape.CIRCLE == 1
True
>>> Shape.CIRCLE == Request.POST
True

하지만 표준 Enum 열거형과는 여전히 비교할 수 없습니다:

>>> class Shape(IntEnum):
...     CIRCLE = 1
...     SQUARE = 2
...
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...
>>> Shape.CIRCLE == Color.RED
False

IntEnum 값은 예상하는 다른 방식들로 정수처럼 동작합니다:

>>> int(Shape.CIRCLE)
1
>>> ['a', 'b', 'c'][Shape.CIRCLE]
'b'
>>> [i for i in range(Shape.SQUARE)]
[0, 1]

StrEnum

제공되는 두 번째 :class:`Enum`의 변형은 :class:`str`의 서브 클래스이기도 합니다. :class:`StrEnum`의 멤버는 문자열과 비교할 수 있으며, 확장하여 서로 다른 유형의 문자열 열거형도 비교할 수 있습니다.

Added in version 3.11.

IntFlag

제공되는 다음 Enum`의 변형인 :class:`IntFlag`는 또한 :class:`int`를 기반으로 합니다. 차이점은, :class:`IntFlag 멤버들은 비트 연산자(&, |, ^, ~)를 사용하여 결합할 수 있으며 그 결과는 가능하다면 여전히 IntFlag 멤버라는 것입니다. :class:`IntEnum`과 마찬가지로, :class:`IntFlag`의 멤버들 또한 정수이며 :class:`int`가 사용되는 모든 곳에서 사용할 수 있습니다.

참고

비트별 연산을 제외한 IntFlag 멤버에 대한 모든 연산은 IntFlag 멤버십을 잃게 합니다.

유효하지 않은 IntFlag 값을 초래하는 비트별 연산은 IntFlag 멤버십을 잃게 됩니다. 자세한 내용은 :class:`FlagBoundary`를 참조하십시오.

Added in version 3.6.

버전 3.11에서 변경.

샘플 IntFlag 클래스:

>>> from enum import IntFlag
>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...
>>> Perm.R | Perm.W
<Perm.R|W: 6>
>>> Perm.R + Perm.W
6
>>> RW = Perm.R | Perm.W
>>> Perm.R in RW
True

명명된 조합을 지정하는 것도 가능합니다:

>>> class Perm(IntFlag):
...     R = 4
...     W = 2
...     X = 1
...     RWX = 7
...
>>> Perm.RWX
<Perm.RWX: 7>
>>> ~Perm.RWX
<Perm: 0>
>>> Perm(7)
<Perm.RWX: 7>

참고

명명된 조합은 별칭으로 간주됩니다. 별칭들은 반복 중에 나타나지는 않지만, 값 조회를 통해 반환될 수 있습니다.

버전 3.11에서 변경.

:class:`IntFlag`와 :class:`Enum`의 또 다른 중요한 차이점은 플래그가 설정되지 않았을 때 (값이 0일 경우), 불리언 평가 결과가 :data:`False`가 된다는 것입니다:

>>> Perm.R & Perm.X
<Perm: 0>
>>> bool(Perm.R & Perm.X)
False

IntFlag 멤버들은 또한 int`의 서브 클래스이므로 정수와 결합할 있습니다 (하지만 :class:`IntFlag 멤버십을 잃을 수 있음):

>>> Perm.X | 4
<Perm.R|X: 5>

>>> Perm.X + 8
9

참고

부정 연산자 ~ 은 항상 양수의 값을 가진 IntFlag 멤버를 반환합니다:

>>> (~Perm.X).value == (Perm.R|Perm.W).value == 6
True

IntFlag 멤버들은 반복할 수도 있습니다:

>>> list(RW)
[<Perm.R: 4>, <Perm.W: 2>]

Added in version 3.11.

플래그

마지막 변형은 Flag`입니다. :class:`IntFlag`와 마찬가지로, :class:`Flag 멤버들은 비트 연산자(&, |, ^, ~)를 사용하여 결합할 수 있습니다. IntFlag`와 달리, 다른 :class:`Flag 열거형이나 :class:`int`와는 결합하거나 비교할 수 없습니다. 값을 직접 지정하는 것도 가능하지만, 값을 :class:`auto`로 사용하고 :class:`Flag`가 적절한 값을 선택하도록 하는 것이 권장됩니다.

Added in version 3.6.

IntFlag`와 마찬가지로, :class:`Flag 멤버의 조합이 아무 플래그도 설정하지 않으면 불리언 평가는 :data:`False`가 됩니다:

>>> from enum import Flag, auto
>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.RED & Color.GREEN
<Color: 0>
>>> bool(Color.RED & Color.GREEN)
False

개별 플래그는 2의 거듭제곱 값(1, 2, 4, 8, …)을 가져야 하지만, 플래그들의 조합은 그렇지 않습니다:

>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...     WHITE = RED | BLUE | GREEN
...
>>> Color.WHITE
<Color.WHITE: 7>

“플래그가 설정되지 않음” 조건에 이름을 지정해도 그 불리언 값은 변경되지 않습니다:

>>> class Color(Flag):
...     BLACK = 0
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.BLACK
<Color.BLACK: 0>
>>> bool(Color.BLACK)
False

Flag 멤버들도 반복할 수 있습니다:

>>> purple = Color.RED | Color.BLUE
>>> list(purple)
[<Color.RED: 1>, <Color.BLUE: 2>]

Added in version 3.11.

참고

For the majority of new code, Enum and Flag are strongly recommended, since IntEnum and IntFlag break some semantic promises of an enumeration (by being comparable to integers, and thus by transitivity to other unrelated enumerations). IntEnum and IntFlag should be used only in cases where Enum and Flag will not do; for example, when integer constants are replaced with enumerations, or for interoperability with other systems.

기타

IntEnum`은 :mod:`enum 모듈의 일부이지만, 독립적으로 구현하기에는 매우 간단할 것입니다:

class IntEnum(int, ReprEnum):   # 또는 ReprEnum 대신 Enum
    pass

This demonstrates how similar derived enumerations can be defined; for example a FloatEnum that mixes in float instead of int.

몇 가지 규칙:

  1. <source>:class:Enum`을 서브 클래싱 할 때, 혼합(mix-in) 유형은 위에 있는 :class:`IntEnum 예제와 같이 베이스 시퀀스에서 Enum 클래스 자체보다 먼저 나와야 합니다.

  2. 믹스인 형은 서브클래싱 가능해야 합니다. 예를 들어, :class:`bool`과 :class:`range`는 서브클래싱이 불가능하며, 믹스인 형식으로 사용될 경우 Enum 생성 시 오류를 발생시킵니다.

  3. Enum`은 어떤 유형의 멤버도 가질 있지만, 추가 유형을 혼합하면 모든 멤버는 해당 유형의 값을 가져야 합니다 (예: 위의 :class:`int). 이 제한 사항은 메서드만 추가하고 다른 유형을 지정하지 않는 믹스인에는 적용되지 않습니다.

  4. 다른 데이터 유형이 혼합될 때, value 속성은 열거형 멤버 자체와 * 같지 않지만*, 동등하며 비교가 가능합니다.

  5. data type__new__() 를 정의하거나, dataclass 인 믹스인입니다.

  6. %-스타일 포매팅: %s%r 은 각각 Enum 클래스의 __str__()__repr__() 을 호출합니다; 다른 코드 (예: IntEnum의 경우 %i 또는 %h)는 열거형 멤버를 혼합된 유형으로 취급합니다.

  7. Formatted string literals, str.format(), 및 format`은 열거형의 :meth:`~object.__str__() 메서드를 사용합니다.

참고

IntEnum, IntFlag, 및 StrEnum`는 기존 상수를 대체하기 위해 설계되었기 때문에, 이들의 :meth:`~object.__str__ 메서드는 각 데이터 유형의 __str__() 메서드로 재설정되었습니다.

:meth:`~object.__new__`와 :meth:`~object.__init__`를 사용할 때

Enum 멤버의 실제 값을 사용자 정의하려면 항상 __new__() 또는 __init__() 둘 중 어디에나 포함될 수 있으며, :meth:`~object.__init__`가 권장됩니다.

예를 들어, 생성자에 여러 항목을 전달하고 싶지만 그중 한 항목만 값을 갖게 하려는 경우:

>>> class Coordinate(bytes, Enum):
...     """
...     int 코드로 인덱싱 가능한 이진 코드 좌표.
...     """
...     def __new__(cls, value, label, unit):
...         obj = bytes.__new__(cls, [value])
...         obj._value_ = value
...         obj.label = label
...         obj.unit = unit
...         return obj
...     PX = (0, 'P.X', 'km')
...     PY = (1, 'P.Y', 'km')
...     VX = (2, 'V.X', 'km/s')
...     VY = (3, 'V.Y', 'km/s')
...

>>> print(Coordinate['PY'])
Coordinate.PY

>>> print(Coordinate(3))
Coordinate.VY

경고

찾기 전용인 __new__super().__new__() 를 호출하지 마세요. 대신, 해당 데이터 유형을 직접 사용하세요.

더 세부적인 사항

지원되는 __dunder___sunder_ 이름

지원되는 __dunder___sunder_ 이름은 :ref:`Enum API documentation <enum-dunder-sunder>`에서 확인할 수 있습니다.

_Private__이름들

:ref:`Private names <private-name-mangling>`은 열거형 멤버로 변환되지 않고 일반 속성으로 유지됩니다.

버전 3.11에서 변경.

Enum 멤버 유형

열거형 멤버는 해당 열거형 클래스의 인스턴스이며, 일반적으로 EnumClass.member 로 액세스됩니다. 자체 정의된 열거형 동작처럼 특정한 상황에서는 한 멤버를 다른 멤버에서 직접 액세스할 수 있는 것이 유용하며 지원되지만, 멤버 이름과 혼합된 클래스의 속성/메서드 간의 이름 충돌을 방지하기 위해 대문자 이름을 사용하는 것을 강력히 권장합니다.

버전 3.5에서 변경.

다른 데이터 유형과 혼합되는 멤버 생성

int 또는 str`와 같은 다른 데이터 유형을 :class:`Enum`으로 서브클래싱할 때, ``=` 뒤의 모든 값은 해당 데이터 유형의 생성자(constructor)로 전달됩니다. 예를 들어 다음과 같습니다:

>>> class MyEnum(IntEnum):      # help(int) -> int(x, base=10) -> integer
...     example = '11', 16      # so x='11' and base=16
...
>>> MyEnum.example.value        # 그리고 hex(11) 이란...
17

Enum 클래스와 멤버의 불리언 값

Enum classes that are mixed with non-Enum types (such as int, str, etc.) are evaluated according to the mixed-in type’s rules; otherwise, all members evaluate as True. To make your own enum’s boolean evaluation depend on the member’s value add the following to your class:

def __bool__(self):
    return bool(self.value)

순수한 Enum 클래스는 항상 :data:`True`로 평가됩니다.

메서드를 가진 Enum 클래스

If you give your enum subclass extra methods, like the Planet class below, those methods will show up in a dir() of the member, but not of the class:

>>> dir(Planet)
['EARTH', 'JUPITER', 'MARS', 'MERCURY', 'NEPTUNE', 'SATURN', 'URANUS', 'VENUS', '__class__', '__doc__', '__members__', '__module__']
>>> dir(Planet.EARTH)
['__class__', '__doc__', '__module__', 'mass', 'name', 'radius', 'surface_gravity', 'value']

Flag 멤버 결합하기

:class:`Flag 멤버 조합을 반복하면 단일 비트로 구성된 멤버만 반환됩니다::`

>>> class Color(Flag):
...     RED = auto()
...     GREEN = auto()
...     BLUE = auto()
...     MAGENTA = RED | BLUE
...     YELLOW = RED | GREEN
...     CYAN = GREEN | BLUE
...
>>> Color(3)  # 이름 지정된 조합
<Color.YELLOW: 3>
>>> Color(7)      # 이름 미지정 조합
<Color.RED|GREEN|BLUE: 7>

FlagIntFlag 의 사소한 차이점::`

다음 코드 조각을 사용해서 예제를 만들 수 있습니다::`

>>> class Color(IntFlag):
...     BLACK = 0
...     RED = 1
...     GREEN = 2
...     BLUE = 4
...     PURPLE = RED | BLUE
...     WHITE = RED | GREEN | BLUE
...

다음 명시 사항들이 사실입니다::`

  • 단일 비트 플래그가 표준입니다::`

  • 다중 비트 및 영비트 플래그는 별칭(alias)입니다::`

  • 반복 중에 표준화된 플래그만 반환됩니다::`

    >>> list(Color.WHITE)
    [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
    
  • 플래그 또는 플래그 세트를 부정하면 해당에 상응하는 양의 정수 값을 가진 새 플래그/플래그 세트가 반환됩니다::`

    >>> Color.BLUE
    <Color.BLUE: 4>
    
    >>> ~Color.BLUE
    <Color.RED|GREEN: 3>
    
  • 유사 플래그의 이름은 해당 멤버의 이름을 기반으로 구성됩니다::`

    >>> (Color.RED | Color.GREEN).name
    'RED|GREEN'
    
    >>> class Perm(IntFlag):
    ...     R = 4
    ...     W = 2
    ...     X = 1
    ...
    >>> (Perm.R & Perm.W).name is None  # 사실상 Perm(0)
    True
    
  • 다중 비트 플래그, 즉 별칭은 연산에서 반환될 수 있습니다::`

    >>> Color.RED | Color.BLUE
    <Color.PURPLE: 5>
    
    >>> Color(7)  # 또는 Color(-1)
    <Color.WHITE: 7>
    
    >>> Color(0)
    <Color.BLACK: 0>
    
  • 멤버십 / 포함 검사: 0으로 설정된 플래그는 항상 포함되는 것으로 간주됩니다::`

    >>> Color.BLACK in Color.WHITE
    True
    

    그렇지 않으면, 한 플래그의 모든 비트가 다른 플래그에 있는 경우에만 True를 반환합니다::`

    >>> Color.PURPLE in Color.WHITE
    True
    
    >>> Color.GREEN in Color.PURPLE
    False
    

사용 범위를 벗어나거나 유효하지 않은 비트를 처리하는 방식을 제어하는 새로운 경계 메커니즘이 있습니다: STRICT, CONFORM, EJECTKEEP::`

  • STRICT –> 유효하지 않은 값을 제시할 때 예외를 발생시킵니다::`

  • CONFORM –> 모든 유효하지 않은 비트를 폐기합니다::`

  • EJECT –> 플래그 상태를 잃고 지정된 값의 일반 정수로 변환합니다::`

  • KEEP –> 추가 비트를 유지합니다::`

    • 플래그 상태와 추가 비트를 모두 보존합니다::`

    • 추가 비트는 반복에서 표시되지 않습니다::`

    • 추가 비트는 repr() 및 str()에서는 표시됩니다::`

Flag의 기본값은 STRICT``이며, ``IntFlag``의 기본값은 ``EJECT``이고, ``_convert_``의 기본값은 ``KEEP``입니다 (필요한 ``KEEP 사용 예는 `ssl.Options`를 참조하십시오).

Enum과 Flag는 어떻게 다릅니까?

열거형은 파생된 Enum 클래스와 그 인스턴스(멤버)의 여러 측면에 영향을 주는 사용자 정의 메타 클래스를 갖습니다::`

Enum 클래스::`

EnumType 메타 클래스는 list(Color)\나 some_enum_var in Color와 같은 일반적인 클래스에서 실패하는 연산을 Enum 클래스로 할 수 있도록 하는 __contains__(), __dir__(), __iter__() 및 기타 메서드를 제공할 책임이 있습니다. EnumType\는 최종 Enum 클래스의 다양한 다른 메서드(가령 __new__(), __getnewargs__(), __str__()__repr__())가 올바른지 확인하는 책임도 있습니다::`

Flag 클래스::`

플래그는 별칭에 대한 확장된 뷰를 갖습니다: 표준화되려면 플래그의 값이 2의 거듭제곱 값이어야 하며, 중복되는 이름이 아니어야 합니다. 따라서 Enum 정의의 별칭 외에도 값이 없는 플래그(즉 0) 또는 하나 이상의 2의 거듭제곱 값을 가진 플래그(예: 3)는 별칭으로 간주됩니다::`

Enum 멤버 (별칭 인스턴스)::`

The most interesting thing about enum members is that they are singletons. EnumType creates them all while it is creating the enum class itself, and then puts a custom __new__() in place to ensure that no new ones are ever instantiated by returning only the existing member instances.

플래그 멤버::`

플래그 멤버는 Flag 클래스와 마찬가지로 반복할 수 있으며, 표준화된 멤버만 반환됩니다. 예:::`

>>> list(Color)
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]

(경고: BLACK, PURPLE, 및 WHITE 는 표시되지 않습니다.)

플래그 멤버를 반전시키면 부정적인 값 대신 해당 양의 값을 반환합니다. 예시:::`

>>> ~Color.RED
<Color.GREEN|BLUE: 6>

플래그 멤버는 포함하는 2의 거듭제곱 값의 수에 해당하는 길이가 있습니다. 예를 들면::`

>>> len(Color.PURPLE)
2

Enum 레시피북::`

Enum, IntEnum, StrEnum, Flag, 및 IntFlag\는 대부분의 사용 사례를 포괄할 것으로 예상되지만, 모든 사용 사례를 포괄할 수는 없습니다. 여기에는 직접 사용할 수 있는 또는 자체 열거형을 만드는 예제로 사용할 수 있는 여러 유형의 열거형에 대한 레시피가 있습니다.

값을 생략하기

많은 사용 사례에서, 열거형의 실제 값이 무엇인지는 중요하지 않습니다. 이런 종류의 간단한 열거형을 정의할 수 있는 여러 가지 방법이 있습니다:

  • 값을 위해 auto 인스턴스를 사용합니다

  • 값으로 object 인스턴스를 사용합니다

  • 값을 설명 문자열로 사용합니다

  • 튜플을 값으로 사용하고 사용자 정의 __new__()\를 사용하여 튜플을 int 값으로 대체하도록 합니다.

이러한 방법 중 어느 것을 사용하든 사용자에게 해당 값이 중요하지 않다는 것을 의미하며, 나머지 멤버의 번호를 다시 매기지 않아도 멤버를 추가하거나 제거하거나 순서를 재배열할 수 있게 합니다.

auto 사용하기

:class:`auto`를 사용하면 다음과 같습니다:

>>> class Color(Enum):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.GREEN
<Color.GREEN: 3>

object 사용하기

:class:`object`를 사용하면 다음과 같습니다:

>>> class Color(Enum):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...
>>> Color.GREEN
<Color.GREEN: <object object at 0x...>>

이것은 또한 자체 :meth:`~object.__repr__``을 작성해야 하는 좋은 예시입니다.

>>> class Color(Enum):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...     def __repr__(self):
...         return "<%s.%s>" % (self.__class__.__name__, self._name_)
...
>>> Color.GREEN
<Color.GREEN>

설명 문자열 사용하기

문자열을 값으로 사용하면 다음과 같습니다:

>>> class Color(Enum):
...     RED = 'stop'
...     GREEN = 'go'
...     BLUE = 'too fast!'
...
>>> Color.GREEN
<Color.GREEN: 'go'>

사용자 정의 __new__() 사용하기

자동 번호 매기기 :meth:`~object.__new__``를 사용하면 다음과 같습니다:

>>> class AutoNumber(Enum):
...     def __new__(cls):
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...
>>> class Color(AutoNumber):
...     RED = ()
...     GREEN = ()
...     BLUE = ()
...
>>> Color.GREEN
<Color.GREEN: 2>

더 범용적인 AutoNumber\를 만들려면, 서명에 *args\를 추가해야 합니다:

>>> class AutoNumber(Enum):
...     def __new__(cls, *args):      # 위에서 변경된 유일한 부분
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
...

그런 다음 AutoNumber\에서 상속할 때, 모든 추가 인자를 처리하기 위해 자체 __init__\를 작성할 수 있습니다:

>>> class Swatch(AutoNumber):
...     def __init__(self, pantone='unknown'):
...         self.pantone = pantone
...     AUBURN = '3497'
...     SEA_GREEN = '1246'
...     BLEACHED_CORAL = () # 아직 Pantone 코드가 없는 새로운 색상!
...
>>> Swatch.SEA_GREEN
<Swatch.SEA_GREEN: 2>
>>> Swatch.SEA_GREEN.pantone
'1246'
>>> Swatch.BLEACHED_CORAL.pantone
'unknown'

참고

__new__() 메서드는 정의된 경우 Enum 멤버 생성 중에 사용됩니다. 그런 다음 이는 클래스 생성 후에 기존 멤버 조회를 위해 사용되는 :meth:`~object.__new__`로 대체됩니다.

경고

super().__new__() \를 호출하지 마세요. 찾아보기 전용 __new__ 가 발견되므로; 대신, 데이터 타입을 직접 사용하세요 – 예:

obj = int.__new__(cls, value)

순서 유지가 가능한 Enum

IntEnum\을 기반으로 하지 않아 정상적인 Enum 불변성(다른 열거형과 비교할 수 없다는 점 등)을 유지하는 순서가 지정된 열거형입니다:

>>> class OrderedEnum(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
...
>>> Grade.C < Grade.A
True

중복 없는 Enum

별칭을 생성하는 대신 중복된 멤버 값이 발견되면 오류를 발생시킵니다:

>>> class DuplicateFreeEnum(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
...
Traceback (most recent call last):
  ...
ValueError: aliases not allowed in DuplicateFreeEnum:  'GRENE' --> 'GREEN'

참고

이것은 열거형을 서브클래싱하여 다른 동작을 추가하거나 변경하고 별칭 생성을 금지하는 유용한 예시입니다. 만약 원하는 유일한 변경 사항이 별칭 생성을 금지하는 것이라면, 대신 unique() 데코레이터를 사용할 수 있습니다.

다중 값 열거형

멤버당 하나 이상의 값을 지원합니다:

>>> class MultiValueEnum(Enum):
...     def __new__(cls, value, *values):
...         self = object.__new__(cls)
...         self._value_ = value
...         for v in values:
...             self._add_value_alias_(v)
...         return self
...
>>> class DType(MultiValueEnum):
...     float32 = 'f', 8
...     double64 = 'd', 9
...
>>> DType('f')
<DType.float32: 'f'>
>>> DType(9)
<DType.double64: 'd'>

행성

__new__()\ 또는 __init__()\가 정의된 경우, 열거형 멤버의 값이 해당 메서드로 전달됩니다:

>>> class Planet(Enum):
...     MERCURY = (3.303e+23, 2.4397e6)
...     VENUS   = (4.869e+24, 6.0518e6)
...     EARTH   = (5.976e+24, 6.37814e6)
...     MARS    = (6.421e+23, 3.3972e6)
...     JUPITER = (1.9e+27,   7.1492e7)
...     SATURN  = (5.688e+26, 6.0268e7)
...     URANUS  = (8.686e+25, 2.5559e7)
...     NEPTUNE = (1.024e+26, 2.4746e7)
...     def __init__(self, mass, radius):
...         self.mass = mass       # 킬로그램 단위 질량
...         self.radius = radius   # 미터 단위 반지름
...     @property
...     def surface_gravity(self):
...         # 만유 중력 상수 (m3 kg-1 s-2)
...         G = 6.67300E-11
...         return G * self.mass / (self.radius * self.radius)
...
>>> Planet.EARTH.value
(5.976e+24, 6378140.0)
>>> Planet.EARTH.surface_gravity
9.802652743337129

시간 주기

_ignore_` 속성 사용 예시:

>>> import datetime as dt
>>> class Period(dt.timedelta, Enum):
...     "different lengths of time"
...     _ignore_ = 'Period i'
...     Period = vars()
...     for i in range(367):
...         Period['day_%d' % i] = i
...
>>> list(Period)[:2]
[<Period.day_0: datetime.timedelta(0)>, <Period.day_1: datetime.timedelta(days=1)>]
>>> list(Period)[-2:]
[<Period.day_365: datetime.timedelta(days=365)>, <Period.day_366: datetime.timedelta(days=366)>]

EnumType 서브클래싱

대부분의 열거형(Enum) 필요는 Enum 서브클래스를 사용자 정의하거나 클래스 데코레이터 또는 사용자 지정 함수를 사용함으로써 충족할 수 있지만, :class:`EnumType`을 서브클래싱하여 다른 Enum 경험을 제공할 수 있습니다.