Python

Enum HOWTO

Enum 은 고유한 값에 바인딩된 기호 이름의 집합입니다. 이들은 전역 변수와 유사하지만, 더 유용한 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

보시는 바와 같이, Enum 을 생성하는 것은 Enum 자체를 상속받는 클래스를 작성하는 것만큼 간단합니다.

참고

Enum 멤버의 사례

Enum은 상수를 나타내는 데 사용되므로, mixin-class 메서드/어트리뷰트와 enum 이름 사이의 이름 충돌 문제를 방지하기 위해 멤버에 대문자(UPPER_CASE) 이름을 사용할 것을 강력히 권장하며, 예제에서도 이 스타일을 사용합니다.

enum의 성격에 따라 멤버의 값이 중요할 수도 있고 그렇지 않을 수도 있지만, 어느 경우든 해당 값을 사용하여 해당하는 멤버를 가져올 수 있습니다:

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

보시는 바와 같이, 멤버의 repr() 는 enum 이름, 멤버 이름, 그리고 값을 보여줍니다. 멤버의 str() 는 enum 이름과 멤버 이름만 표시합니다:

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

enum 멤버의 형(type) 은 해당 멤버가 속한 enum입니다:

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

Enum 멤버는 그들의 name 만 포함하는 어트리뷰트를 가집니다:

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

마찬가지로, 그들은 value 를 위한 어트리뷰트를 가집니다:

>>> Weekday.WEDNESDAY.value
3

enum을 단순히 이름/값 쌍으로만 취급하는 많은 언어들과 달리, Python Enum은 동작을 추가할 수 있습니다. 예를 들어, datetime.date 는 요일을 반환하는 두 가지 메서드인 weekday()isoweekday() 를 가지고 있습니다. 차이점은 하나는 0-6으로 세고 다른 하나는 1-7로 센다는 것입니다. 이를 직접 추적하는 대신, 우리는 Weekday enum에 메서드를 추가하여 date 인스턴스로부터 요일을 추출하고 일치하는 enum 멤버를 반환하게 할 수 있습니다:

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

완성된 Weekday enum은 이제 다음과 같습니다:

>>> 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 enum은 변수가 하나의 요일만 필요할 때 아주 유용하지만, 여러 개가 필요한 경우는 어떠한가요? 예를 들어 한 주 동안의 집안일을 기록하는 함수를 작성 중인데 list 를 사용하고 싶지 않다면, 다른 유형의 Enum 을 사용할 수 있습니다:

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

두 가지를 변경했습니다. 바로 Flag 를 상속받았으며, 모든 값은 2의 거듭제곱입니다.

위의 원래 Weekday enum과 마찬가지로, 단일 선택을 할 수 있습니다:

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

하지만 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

멤버의 실제 값이 중요하지 않은 경우, 작업량을 줄이기 위해 값에 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

enum 멤버와 그들의 어트리뷰트에 대한 프로그래밍 방식의 접근

때로는 프로그래밍 방식으로 enum 멤버에 접근하는 것이 유용합니다(예를 들어, 프로그램 작성 시점에 정확한 색상을 알 수 없어 Color.RED 를 사용할 수 없는 경우). Enum 은 그러한 접근을 허용합니다:

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

enum 멤버에 이름 으로 접근하려면 아이템 접근(item access)을 사용하십시오:

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

enum 멤버를 가지고 있으며 그 멤버의 name`이나 :attr:!value`가 필요한 경우:

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

enum 멤버와 값 중복하기

동일한 이름을 가진 두 개의 enum 멤버를 갖는 것은 유효하지 않습니다:

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

그러나, 하나의 enum 멤버는 다른 이름들과 연결될 수 있습니다. 동일한 값을 가진 두 항목 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>

참고

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

고유한 열거형 값 보장

기본적으로, enum은 동일한 값에 대해 여러 개의 이름을 별칭으로 허용합니다. 이 동작을 원하지 않을 때는 @unique 데코레이터를 사용할 수 있습니다:

>>> from enum import Enum, unique
>>> @unique
... class Mistake(Enum):
...     ONE = 1
...     TWO = 2
...     THREE = 3
...     FOUR = 3
...
Traceback (most recent call last):
...
ValueError: duplicate values found in <enum 'Mistake'>: FOUR -> THREE

자동 값 사용하기

정확한 값이 중요하지 않다면 auto 를 사용할 수 있습니다:

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

값은 _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)

enum의 멤버를 반복할 때 별칭은 제공되지 않습니다:

>>> 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__ 는 이름에서 멤버로 가는 읽기 전용 순서 있는 매핑입니다. 이는 별칭을 포함하여 enum에 정의된 모든 이름을 포함합니다:

>>> 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__ 어트리뷰트는 enum 멤버에 대한 상세한 프로그래밍 방식의 접근에 사용될 수 있습니다. 예를 들어, 모든 별칭을 찾는 경우:

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

참고

플래그(flag)의 별칭에는 3 과 같이 여러 플래그가 설정된 값이나 0 과 같이 아무것도 설정되지 않은 값이 포함될 수 있습니다.

비교

enum 멤버들은 아이덴티티(identity)에 의해 비교됩니다:

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

enum 값 간의 순서 기반 비교는 지원되지 않습니다. 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'

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

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

enum이 아닌 값과 비교할 때는 항상 False 를 반환합니다 (다시 강조하지만, IntEnum 은 다르게 동작하도록 명확하게 설계되었습니다. 아래 참조):

>>> Color.BLUE == 2
False

경고

모듈을 다시 로드하는 것이 가능하며 – 재로드된 모듈에 enum이 포함되어 있다면 이들은 다시 생성되며, 새 멤버는 원본 멤버와 동일하거나 같지 않을 수 있습니다.

enum의 허용되는 멤버와 어트리뷰트

위의 대부분 예제는 enum 값으로 정수를 사용합니다. 정수를 사용하는 것은 짧고 편리하지만(또한 Functional API 에 의해 기본으로 제공되지만), 엄격하게 강제되지는 않습니다. 대다수의 경우, 우리는 enum의 실제 값이 무엇인지 신경 쓰지 않습니다. 하지만 그 값이 중요하다면, enum은 임의의 값을 가질 수 있습니다.

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

>>> class Mood(Enum):
...     FUNKY = 1
...     HAPPY = 3
...
...     def describe(self):
...         # 여기서 self는 멤버입니다
...         return self.name, self.value
...
...     def __str__(self):
...         return 'my custom str! {0}'.format(self.value)
...
...     @classmethod
...     def favorite_mood(cls):
...         # 여기서 cls는 enum입니다
...         return cls.HAPPY
...

그다음:

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

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

참고: 여러분의 enum이 __new__() 및/또는 __init__() 를 정의하는 경우, enum 멤버에 전달되는 모든 값이 해당 메서드들로 전달됩니다. 예시는 Planet 을 참조하십시오.

참고

정의된 경우 __new__() 메서드는 Enum 멤버 생성 중에 사용되며; 그 후 클래스 생성 이후 기존 멤버를 조회하기 위해 사용되는 Enum의 __new__() 로 대체됩니다. 자세한 내용은 __init__() 중 언제 사용하는지 을 참조하십시오.

제한된 Enum 서브 클래싱

새로운 Enum 클래스는 하나의 베이스 enum 클래스, 최대 하나의 구체적인 데이터 타입, 그리고 필요한 만큼의 object 기반 mixin 클래스를 가져야 합니다. 이 베이스 클래스들의 순서는 다음과 같습니다:

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

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

>>> 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의 서브 클래싱을 허용하면 타입과 인스턴스의 몇 가지 중요한 불변성을 위반하게 됩니다. 반면에, 여러 enum 간에 공통된 동작을 공유하는 것을 허용하는 것은 타당합니다. (예시로 OrderedEnum 를 참조하십시오.)

Dataclass 지원

어느 dataclass 로부터 상속받을 때, __repr__() 는 상속된 클래스의 이름을 생략합니다. 예를 들어:

>>> 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의 이름은 표시되지 않습니다.

참고

Enum 및 그 하위 클래스에 @~dataclasses.dataclass 데코레이터를 추가하는 것은 지원되지 않습니다. 오류가 발생하지는 않지만, 런타임에서 멤버들이 서로 동일하게 처리되는 등 매우 이상한 결과가 나타날 수 있습니다:

>>> @dataclass               # 하지 마세요: 아무런 의미가 없습니다
... class Color(Enum):
...    RED = 1
...    BLUE = 2
...
>>> Color.RED is Color.BLUE
False
>>> Color.RED == Color.BLUE  # 문제가 발생한 지점: 이들은 서로 같아서는 안 됩니다
True

피클링

열거형은 피클(pickle) 및 역피클(unpickle)이 가능합니다:

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

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

참고

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

열거형 클래스에 __reduce_ex__() 를 정의하여 열거형 멤버가 피클/역피클되는 방식을 수정할 수 있습니다. 기본 방식은 값(by-value) 기반이지만, 복잡한 값을 가진 열거형은 이름(by-name) 기반을 사용하고 싶을 수 있습니다:

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

참고

이름 없는 별칭이 역피클되지 않으므로 플래그에 대해 이름(by-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의 의미론은 namedtuple 과 유사합니다. Enum 호출의 첫 번째 인자는 열거형의 이름입니다.

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

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

시작 번호를 0 이 아닌 1 로 기본 설정하는 이유는 0 이 불리언 의미에서 False 이지만, 기본적으로 열거형 멤버는 모두 True 로 평가되기 때문입니다.

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

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

경고

module 이 제공되지 않고 Enum이 이를 파악할 수 없는 경우, 새 Enum 멤버는 역피클 가능하지 않습니다. 오류를 소스에 가깝게 유지하기 위해 피클링은 비활성화됩니다.

새로운 피클 프로토콜 4는 일부 상황에서 피클이 클래스를 찾을 수 있는 위치로 __qualname__ 이 설정되어 있음에 의존합니다. 예를 들어, 클래스가 전역 범위의 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: Enum 멤버. 이는 공백이나 쉼표로 구분된 문자열일 수 있으며, 별도로 지정하지 않는 한 값이 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}
    
  • module: 새 Enum 클래스를 찾을 수 있는 모듈의 이름.

  • qualname: 모듈 내에서 새로운 Enum 클래스를 찾을 수 있는 위치.

  • type: 새 Enum 클래스에 믹스인(mix-in)할 형.

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

버전 3.5에서 변경: start 매개변수가 추가되었습니다.

파생된 열거형

IntEnum

제공되는 첫 번째 Enum 변형은 int 의 하위 클래스이기도 합니다. 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

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

Added in version 3.11.

IntFlag

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

참고

비트 연산 이외의 작업을 IntFlag 멤버에 수행하면 IntFlag 소속을 잃게 됩니다.

유효하지 않은 IntFlag 값을 생성하는 비트 연산은 IntFlag 소속을 잃게 만듭니다. 자세한 내용은 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에서 변경.

IntFlagEnum 의 또 다른 중요한 차이점은 어떤 플래그도 설정되지 않았을 때(값이 0인 경우) 불리언 평가 결과가 False 라는 점입니다:

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

IntFlag 멤버도 int 의 하위 클래스이므로 정수와 결합할 수 있습니다(그러나 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 입니다. IntFlag 와 마찬가지로, Flag 멤버는 비트 연산자(&, |, ^, ~)를 사용하여 결합할 수 있습니다. IntFlag 와 달리, 다른 Flag 열거형이나 int 와 결합하거나 비교할 수 없습니다. 값을 직접 지정하는 것도 가능하지만, 값으로 auto 를 사용하고 Flag 가 적절한 값을 선택하도록 하는 것이 권장됩니다.

Added in version 3.6.

IntFlag 와 마찬가지로, Flag 멤버의 조합이 어떤 플래그도 설정하지 않은 결과(값이 0)를 내면 불리언 평가가 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.

참고

대부분의 새로운 코드에서는 EnumFlag 를 사용할 것을 강력히 권장합니다. IntEnumIntFlag 는 정수와 비교 가능하기 때문에(그에 따른 추이성으로 인해 다른 관련 없는 열거형과도 비교될 수 있음) 열거형의 일부 의미론적 약속을 깨뜨리기 때문입니다. IntEnumIntFlagEnumFlag 를 사용할 수 없는 경우에만 사용해야 합니다(예: 정수 상수가 열거형으로 대체되는 경우나 다른 시스템과의 상호 운용성이 필요한 경우 등).

기타

IntEnumenum 모듈의 일부이지만, 독자적으로 구현하는 것도 매우 간단합니다:

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

이는 유사한 파생 열거형을 정의하는 방법을 보여줍니다. 예를 들어, int 대신 float`를 믹스인(mix-in)하는 :class:!FloatEnum`이 있습니다.

일부 규칙:

  1. Enum 을 서브 클래싱할 때, 위 IntEnum 예제와 같이 베이스 시퀀스에서 믹스인 유형이 Enum 클래스보다 앞에 위치해야 합니다.

  2. 믹스인 유형은 서브 클래싱이 가능해야 합니다. 예를 들어, boolrange 는 서브 클래싱이 불가능하므로 믹스인 유형으로 사용할 경우 열거형 생성 시 오류가 발생합니다.

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

  4. 다른 데이터 형이 믹스인될 때, value 속성은 열거형 멤버 자체와 동일하지 않지만, 등가이며 비교 시 동일하게 처리됩니다.

  5. 데이터 형식(data type)__new__() 를 정의하는 믹스인 또는 dataclass 입니다.

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

  7. 포맷 문자열 리터럴, str.format(), 그리고 format() 은 열거형의 __str__() 메서드를 사용합니다.

참고

IntEnum, IntFlag, 그리고 StrEnum 은 기존 상수를 대체하도록 설계되었으므로, 이들의 __str__() 메서드는 해당 데이터 타입의 __str__() 메서드로 재설정되었습니다.

__init__() 중 언제 사용하는지

Enum 멤버의 실제 값을 사용자 정의하려면 반드시 __new__() 를 사용해야 합니다. 그 외의 수정 사항은 __new__() 또는 __init__() 에 포함할 수 있으며, __init__() 을 사용하는 것이 권장됩니다.

예를 들어, 생성자에 여러 항목을 전달하지만 그중 하나만 값으로 사용하고 싶은 경우:

>>> class Coordinate(bytes, Enum):
...     """
...     정수 코드로 인덱싱할 수 있는 바이너리 코드를 가진 Coordinate.
...     """
...     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__ 이름은 Enum API 문서 에서 확인할 수 있습니다.

_Private__이름

개인 이름 은 열거형 멤버로 변환되지 않고 일반 속성으로 유지됩니다.

버전 3.11에서 변경.

Enum 멤버 타입

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

버전 3.5에서 변경.

다른 데이터 타입과 혼합된 멤버 생성

intstr 와 같은 다른 데이터 타입을 기반으로 하여 Enum 을 서브 클래스로 구현할 때, = 뒤의 모든 값이 해당 데이터 타입의 생성자로 전달됩니다. 예를 들어:

>>> class MyEnum(IntEnum):      # help(int) -> int(x, base=10) -> 정수
...     example = '11', 16      # 따라서 x='11'이고 base=16
...
>>> MyEnum.example.value        # 그리고 hex(11)은...
17

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

Enum 타입(가령 int, str 등)과 혼합된 Enum 클래스는 해당 혼합형 타입의 규칙에 따라 평가되며, 그렇지 않은 경우 모든 멤버는 True 로 평가됩니다. 사용자 정의 Enum의 불리언 평가를 멤버의 값에 따르게 하려면 클래스에 다음을 추가하십시오:

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

일반 Enum 클래스는 항상 True 로 평가됩니다.

메서드가 포함된 Enum 클래스

아래 Planet 클래스처럼 Enum 서브 클래스에 추가 메서드를 제공하면, 해당 메서드는 멤버의 dir() 에는 나타나지만 클래스에서는 나타나지 않습니다:

>>> 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 멤버 결합

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
...

다음 사항이 참입니다:

  • 단일 비트 플래그가 표준(canonical)입니다.

  • 다중 비트 및 제로 비트 플래그는 별칭입니다.

  • 반복 시에는 표준 플래그만 반환됩니다:

    >>> list(Color.WHITE)
    [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
    
  • 플래그 또는 플래그 집합을 부정하면 해당하는 양의 정수 값을 가진 새로운 플래그/플래그 집합이 반환됩니다:

    >>> Color.BLUE
    <Color.BLUE: 4>
    
    >>> ~Color.BLUE
    <Color.RED|GREEN: 3>
    
  • 가짜(pseudo) 플래그의 이름은 멤버의 이름으로부터 구성됩니다:

    >>> (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, EJECT, KEEP

  • STRICT –> 잘못된 값이 제시될 때 예외를 발생시킴

  • CONFORM –> 모든 잘못된 비트를 버림

  • EJECT –> Flag 상태를 잃고 주어진 값의 일반 정수(int)가 됨

  • KEEP –> 추가 비트를 유지함

    • Flag 상태와 추가 비트를 모두 유지함

    • 반복 시에는 추가 비트가 나타나지 않음

    • 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 멤버(인스턴스)

Enum 멤버의 가장 흥미로운 점은 이들이 싱글톤이라는 것입니다. EnumType 은 Enum 클래스를 생성하는 동안 모든 멤버를 생성하며, 그 후 사용자 정의 __new__() 을 배치하여 기존 멤버 인스턴스만 반환함으로써 새로운 인스턴스가 절대 생성되지 않도록 보장합니다.

Flag 멤버

Flag 멤버는 Flag 클래스와 마찬가지로 반복할 수 있으며, 이때 표준(canonical) 멤버만 반환됩니다. 예를 들어:

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

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

플래그 멤버를 반전시키면 음수 대신 해당 양의 값이 반환됩니다. 예를 들어:

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

Flag 멤버는 포함된 2의 거듭제곱 값의 수에 해당하는 길이를 가집니다. 예를 들어:

>>> len(Color.PURPLE)
2

Enum 요리법

Enum, IntEnum, StrEnum, Flag, 그리고 IntFlag 가 대부분의 사용 사례를 충족할 것으로 예상되지만, 모든 경우를 해결할 수는 없습니다. 여기에는 직접 사용할 수 있는 몇 가지 다른 유형의 열거형 또는 자신만의 것을 만들기 위한 예시로 활용할 수 있는 조리법이 있습니다.

값 생략

많은 경우에 열거형의 실제 값이 무엇인지는 중요하지 않습니다. 이러한 유형의 간단한 열거형을 정의하는 몇 가지 방법이 있습니다:

  • 값으로 auto 인스턴스 사용

  • 값으로 object 인스턴스 사용

  • 설명적인 문자열을 값으로 사용

  • 튜플을 값으로 사용하고 사용자 정의 __new__() 를 사용하여 튜플을 int 값으로 대체

이러한 방법 중 하나를 사용하는 것은 사용자에게 해당 값이 중요하지 않음을 의미하며, 나머지 멤버의 번호를 다시 매길 필요 없이 멤버를 추가, 제거 또는 재정렬할 수 있게 해줍니다.

auto 사용

auto 를 사용하면 다음과 같은 형태가 됩니다:

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

object 사용

object 를 사용하면 다음과 같은 형태가 됩니다:

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

이는 왜 자신만의 __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__() 사용하기

자동 번호 매기기 __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 멤버 생성 중에 사용되며, 이후에는 기존 멤버 조회를 위해 클래스 생성 후에 호출되는 Enum의 __new__() 로 대체됩니다.

경고

조회 전용(lookup-only) __new__ 가 선택되므로 super().__new__() 를 호출하지 마십시오. 대신 데이터 타입을 직접 사용하십시오. 예:

obj = int.__new__(cls, value)

OrderedEnum

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

DuplicateFreeEnum

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

>>> 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'

참고

이는 별칭을 금지할 뿐만 아니라 다른 동작을 추가하거나 변경하기 위해 Enum을 서브클래싱하는 유용한 예시입니다. 만약 별칭을 금지하는 것이 유일한 목적이라면 대신 unique() 데코레이터를 사용할 수 있습니다.

MultiValueEnum

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

>>> 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'>

Planet

__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       # 킬로그램(kg) 단위
...         self.radius = radius   # 미터(m) 단위
...     @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

TimePeriod

_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 을 서브클래싱하여 충족할 수 있지만, EnumType 을 서브클래싱하여 다른 방식의 열거형 경험을 제공할 수도 있습니다.

분실물 보관소