enum --- 열거형 지원

버전 3.4에 추가.

소스 코드: Lib/enum.py


열거형(enumeration)은 고유한 상숫값에 연결된 기호 이름(멤버)의 집합입니다. 열거형 내에서, 멤버를 아이덴티티로 비교할 수 있고, 열거형 자체는 이터레이트 될 수 있습니다.

모듈 내용

이 모듈은 고유한 이름 집합과 값을 정의하는 데 사용할 수 있는 네 가지 열거형 클래스를 정의합니다: Enum, IntEnum, FlagIntFlag. 또한 하나의 데코레이터 unique()와 하나의 도우미 auto를 정의합니다.

class enum.Enum

열거형 상수를 만들기 위한 베이스 클래스. 대체 구성 문법은 함수형 API 섹션을 참조하십시오.

class enum.IntEnum

int의 서브 클래스이기도 한 열거형 상수를 만들기 위한 베이스 클래스.

class enum.IntFlag

IntFlag 멤버십을 잃지 않고 비트 연산자를 사용하여 결합할 수 있는 열거형 상수를 만들기 위한 베이스 클래스. IntFlag 멤버도 int의 서브 클래스입니다.

class enum.Flag

Flag 멤버십을 잃지 않고 비트 연산을 사용하여 결합할 수 있는 열거형 상수를 만들기 위한 베이스 클래스.

enum.unique()

한 값에 하나의 이름 만 연결되도록 하는 Enum 클래스 데코레이터.

class enum.auto

인스턴스는 Enum 멤버에 적절한 값으로 바뀝니다. 기본적으로, 초깃값은 1부터 시작합니다.

버전 3.6에 추가: Flag, IntFlag, auto

Enum 만들기

열거형은 class 문법을 사용하여 작성되므로 쉽게 읽고 쓸 수 있습니다. 대체 작성 방법은 함수형 API에 설명되어 있습니다. 열거형을 정의하려면, 다음과 같이 Enum을 서브 클래스 하십시오:

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

참고

Enum 멤버 값

멤버 값은 아무것이나 될 수 있습니다: int, str 등. 정확한 값이 중요하지 않다면, auto 인스턴스를 사용할 수 있으며 적절한 값이 선택됩니다. auto를 다른 값과 혼합 할 경우 주의를 기울여야 합니다.

참고

명명법

  • Color 클래스는 열거형(enumeration) (또는 enum) 입니다.

  • Color.RED, Color.GREEN 등의 어트리뷰트는 열거형 멤버(enumeration members)(또는 enum members)이며 기능상 상수입니다.

  • 열거형 멤버에는 이름(names)값(values)이 있습니다 (Color.RED의 이름은 RED, Color.BLUE의 값은 3, 등)

참고

class 문법을 사용하여 Enum을 만들더라도, Enum은 일반적인 파이썬 클래스가 아닙니다. 자세한 내용은 열거형은 어떻게 다릅니까? 를 참조하십시오.

열거형 멤버는 사람이 읽을 수 있는 문자열 표현을 갖습니다:

>>> print(Color.RED)
Color.RED

repr에는 더 자세한 정보가 있습니다:

>>> print(repr(Color.RED))
<Color.RED: 1>

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

>>> type(Color.RED)
<enum 'Color'>
>>> isinstance(Color.GREEN, Color)
True
>>>

Enum 멤버에는 항목 이름 만 포함하는 프로퍼티가 있습니다:

>>> print(Color.RED.name)
RED

열거형은 정의 순서로 이터레이션을 지원합니다:

>>> class Shake(Enum):
...     VANILLA = 7
...     CHOCOLATE = 4
...     COOKIES = 9
...     MINT = 3
...
>>> for shake in Shake:
...     print(shake)
...
Shake.VANILLA
Shake.CHOCOLATE
Shake.COOKIES
Shake.MINT

열거형 멤버는 해시 가능하므로, 딕셔너리와 집합에 사용할 수 있습니다:

>>> apples = {}
>>> apples[Color.RED] = 'red delicious'
>>> apples[Color.GREEN] = 'granny smith'
>>> apples == {Color.RED: 'red delicious', Color.GREEN: 'granny smith'}
True

열거형 멤버와 그들의 어트리뷰트에 프로그래밍 방식으로 액세스하기

때로는 프로그래밍 방식으로 열거형의 멤버에 액세스하는 것이 유용합니다 (즉, 프로그램 작성 시간에 정확한 색상을 알 수 없어서 Color.RED를 쓸 수 없는 상황). Enum는 그런 액세스를 허용합니다:

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

이름(name)으로 열거형 멤버에 액세스하려면, 항목 액세스를 사용하십시오:

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

열거형 멤버가 있고 name이나 value가 필요하면:

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

열거형 멤버와 값 중복하기

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

>>> class Shape(Enum):
...     SQUARE = 2
...     SQUARE = 3
...
Traceback (most recent call last):
...
TypeError: Attempted to reuse key: 'SQUARE'

그러나, 두 열거형 멤버는 같은 값을 가질 수 있습니다. 같은 값을 가진 두 멤버 A와 B가 주어지면 (그리고 A가 먼저 정의되면), B는 A의 별칭입니다. A와 B의 값을 통한 조회는 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

열거형 용 class 데코레이터입니다. 열거형의 __members__를 검색하여 별칭을 수집합니다; 발견되면 ValueError가 세부 정보와 함께 발생합니다:

>>> 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()
...
>>> list(Color)
[<Color.RED: 1>, <Color.BLUE: 2>, <Color.GREEN: 3>]

값은 _generate_next_value_()에 의해 선택되는데, 재정의할 수 있습니다:

>>> class AutoName(Enum):
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]

참고

기본 _generate_next_value_() 메서드의 목표는 제공된 마지막 int와 연속되도록 다음 int를 제공하는 것이지만, 이를 수행하는 방법은 구현 세부 사항이며 변경될 수 있습니다.

참고

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

이터레이션

열거형 멤버를 이터레이트 해도 별칭은 제공되지 않습니다:

>>> list(Shape)
[<Shape.SQUARE: 2>, <Shape.DIAMOND: 1>, <Shape.CIRCLE: 3>]

특수 어트리뷰트 __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']

비교

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

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

열거형 값 사이의 순서 비교는 지원되지 않습니다. 열거형 멤버는 정수가 아닙니다 (그러나 아래의 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

열거형 값이 아닌 값과의 비교는 항상 다르다고 비교됩니다 (다시, IntEnum은 다르게 동작하도록 명시적으로 설계되었습니다, 아래를 참조하십시오):

>>> Color.BLUE == 2
False

열거형의 허용된 멤버와 어트리뷰트

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

열거형은 파이썬 클래스이며, 평소와 같이 메서드와 특수 메서드를 가질 수 있습니다. 이런 열거형이 있다고 합시다:

>>> 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는 열거형입니다
...         return cls.HAPPY
...

그러면:

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

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

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

제한된 Enum 서브 클래싱

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

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

또한, 열거형의 서브 클래싱은 열거형이 멤버를 정의하지 않았을 때만 허용됩니다. 따라서 다음과 같은 것은 금지되어 있습니다:

>>> class MoreColor(Color):
...     PINK = 17
...
Traceback (most recent call last):
...
TypeError: Cannot extend enumerations

그러나 이것은 허용됩니다:

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

멤버를 정의하는 열거형의 서브 클래싱을 허용하면 형과 인스턴스의 중요한 불변성을 위반하게 됩니다. 반면에, 열거형 그룹 간에 공통적인 동작을 공유하도록 허락하는 것은 말이 됩니다. (예는 OrderedEnum을 참조하십시오.)

피클링

열거형은 피클링 되거나 역 피클링 될 수 있습니다:

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

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

참고

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

열거형 클래스에 __reduce_ex__()를 정의하여 Enum 멤버를 피클/역 피클 하는 방법을 수정할 수 있습니다.

함수형 API

Enum 클래스는 다음과 같은 함수형 API를 제공하는 콜러블입니다:

>>> Animal = Enum('Animal', 'ANT BEE CAT DOG')
>>> Animal
<enum 'Animal'>
>>> Animal.ANT
<Animal.ANT: 1>
>>> Animal.ANT.value
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__이 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

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 클래스와 혼합할 형.

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]

IntFlag

제공된 Enum의 다음 변형인 IntFlagint를 기반으로 합니다. 차이점은, IntFlag 멤버는 비트 연산자(&, |, ^, ~)를 사용하여 결합할 수 있으며 결과는 여전히 IntFlag 멤버라는 것입니다. 그러나, 이름에서 알 수 있듯이, IntFlag 멤버는 int를 서브 클래스하고 int가 사용되는 모든 곳에서 사용할 수 있습니다. 비트별 연산 이외의 IntFlag 멤버에 대한 모든 연산은 IntFlag 멤버 자격을 잃게 만듭니다.

버전 3.6에 추가.

예제 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.-8: -8>

IntFlagEnum의 또 다른 중요한 차이점은 아무런 플래그도 설정되지 않으면 (값이 0입니다) 불리언 평가가 False가 된다는 것입니다:

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

IntFlag 멤버도 int의 서브 클래스이므로 정수와 결합할 수 있습니다:

>>> Perm.X | 8
<Perm.8|X: 9>

Flag

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

버전 3.6에 추가.

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

>>> from enum import Flag, auto
>>> class Color(Flag):
...     RED = auto()
...     BLUE = auto()
...     GREEN = auto()
...
>>> Color.RED & Color.GREEN
<Color.0: 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

참고

IntEnumIntFlag는 열거형에 대한 의미론적 약속을 깨뜨리기 때문에 (정수와 비교할 수 있어서, 다른 관련되지 않은 열거형으로의 추이성(transitivity)으로 인해), 새로운 코드 대부분에는 EnumFlag를 강력히 권장합니다. IntEnumIntFlagEnumFlag가 동작하지 않는 경우에만 사용해야 합니다; 예를 들어, 정수 상수가 열거형으로 대체되거나, 다른 시스템과의 상호 운용성을 위해.

기타

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

class IntEnum(int, Enum):
    pass

이것은 유사한 파생된 열거형을 정의 할 수 있는 방법을 보여줍니다; 예를 들어 int 대신 str로 혼합되는 StrEnum.

몇 가지 규칙:

  1. Enum을 서브 클래싱 할 때, 위의 IntEnum 예제에서처럼, 혼합(mix-in)형은 베이스 시퀀스에서 Enum 앞에 나타나야 합니다.

  2. Enum은 모든 형의 멤버를 가질 수 있지만, 일단 추가 형을 혼합하면, 모든 멤버는 해당 형의 값을 가져야 합니다, 예를 들어 위의 int. 이 제한은 메서드만 추가할 뿐 다른 형을 지정하지 않는 믹스인에는 적용되지 않습니다.

  3. 다른 데이터형이 혼합될 때, value 어트리뷰트는 열거형 멤버 자체와 같지 않지만, 동등하고 같다고 비교됩니다.

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

  5. 포맷 문자열 리터럴, str.format()format()은 혼합형의 __format__()을 사용합니다. 하지만, 서브 클래스에서 __str__()이나 __format__()이 재정의되면, 재정의된 메서드나 Enum 메소드가 사용됩니다. Enum 클래스의 __str__()__repr__() 메서드의 사용을 강제하려면 !s 과 !r 포맷 코드를 사용하십시오.

__new__()__init__()를 사용할 때

Enum 멤버의 실제 값을 사용자 정의하려면 __new__()를 사용해야 합니다. 다른 수정은 __new__()__init__()를 사용할 수 있지만, __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

흥미로운 예

Enum, IntEnum, IntFlagFlag는 대부분의 사용 사례를 포괄할 것으로 예상되지만, 모든 사용 사례를 포괄할 수는 없습니다. 다음은 직접 혹은 자신의 것을 만드는 예제로 사용할 수 있는 여러 유형의 열거형에 대한 조리법입니다.

값 생략하기

많은 사용 사례에서 열거형의 실제 값이 무엇인지 신경 쓰지 않습니다. 이런 유형의 간단한 열거형을 정의하는 몇 가지 방법이 있습니다:

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

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

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

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

이러한 방법의 하나를 사용하는 것은 사용자에게 이러한 값이 중요하지 않다고 알리고, 나머지 멤버의 번호를 다시 매길 필요 없이 멤버를 추가, 제거 또는 재정렬 할 수 있도록 합니다.

어떤 방법을 선택하든, (중요하지 않은) 값을 숨기는 repr()을 제공해야 합니다:

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

auto 사용하기

auto를 사용하면 이렇게 됩니다:

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

object 사용하기

object를 사용하면 이렇게 됩니다:

>>> class Color(NoValue):
...     RED = object()
...     GREEN = object()
...     BLUE = object()
...
>>> Color.GREEN
<Color.GREEN>

설명 문자열 사용하기

문자열을 값으로 사용하면 이렇게 됩니다:

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

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

자동 번호 매기기 __new__()를 사용하면 이렇게 됩니다:

>>> class AutoNumber(NoValue):
...     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>
>>> Color.GREEN.value
2

참고

정의되면, __new__() 메서드는 Enum 멤버 생성 중에 사용됩니다; 그런 다음 Enum의 __new__()로 대체되는데, 이것이 클래스 생성 후에 기존 멤버를 조회하기 위해 사용됩니다.

OrderedEnum

IntEnum에 기반하지 않기 때문에 일반적인 Enum 불변성(invariants) (가령 다른 열거형과 비교할 수 없다는 성질) 을 유지하는 순서 있는 열거형:

>>> 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() 데코레이터를 대신 사용할 수 있습니다.

행성

__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       # in kilograms
...         self.radius = radius   # in meters
...     @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_ 어트리뷰트의 사용을 보여주는 예:

>>> from datetime import timedelta
>>> class Period(timedelta, Enum):
...     "시간의 다른 길이"
...     _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)>]

열거형은 어떻게 다릅니까?

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

열거형 클래스

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

열거형 멤버 (일명 인스턴스)

Enum 멤버에 대한 가장 흥미로운 점은 싱글톤이라는 것입니다. EnumMetaEnum 클래스 자체를 만드는 동안 멤버를 모두 만든 다음, 사용자 정의 __new__()를 넣어서 기존 멤버 인스턴스만 반환하여 더는 새 인스턴스가 만들어지지 않도록 합니다.

세부 사항

지원되는 __dunder__ 이름

__members__member_name:member 항목의 읽기 전용 순서 있는 매핑입니다. 클래스에서만 이용할 수 있습니다.

지정된다면, __new__()는 열거형 멤버를 만들고 반환해야 합니다; 멤버의 _value_를 적절하게 설정하는 것도 좋습니다. 일단 모든 멤버가 만들어지면 더는 사용되지 않습니다.

지원되는 _sunder_ 이름

  • _name_ -- 멤버의 이름

  • _value_ -- 멤버의 값; __new__에서 설정/수정할 수 있습니다

  • _missing_ -- 값을 찾을 수 없을 때 사용되는 조회 함수; 재정의할 수 있습니다

  • _ignore_ -- 멤버로 변환되지 않고 최종 클래스에서 제거될 liststr 형의 이름 목록

  • _order_ -- 파이썬 2/3 코드에서 멤버 순서의 일관성을 유지하기 위해 사용됩니다 (클래스 생성 중 제거되는 클래스 어트리뷰트)

  • _generate_next_value_ -- 열거형 멤버에 대한 적절한 값을 얻기 위해 함수형 APIauto에서 사용합니다; 재정의할 수 있습니다

버전 3.6에 추가: _missing_, _order_, _generate_next_value_

버전 3.7에 추가: _ignore_

파이썬 2 / 파이썬 3 코드를 동기화 상태로 유지하기 위해 _order_ 어트리뷰트를 제공 할 수 있습니다. 열거형의 실제 순서와 비교하여 확인되며 일치하지 않으면 에러가 발생합니다:

>>> class Color(Enum):
...     _order_ = 'RED GREEN BLUE'
...     RED = 1
...     BLUE = 3
...     GREEN = 2
...
Traceback (most recent call last):
...
TypeError: member order does not match _order_

참고

파이썬 2 코드에서는 정의 순서가 기록될 수 있기 전에 손실되기 때문에 _order_ 어트리뷰트가 필요합니다.

Enum 멤버 형

Enum 멤버는 Enum 클래스의 인스턴스이며, 일반적으로 EnumClass.member로 액세스 됩니다. 특정 상황에서는 EnumClass.member.member로 액세스 할 수 있지만, 조회가 실패하거나 더 나쁜 경우 찾고 있는 Enum 멤버 이외의 것을 반환할 수 있기 때문에 이 작업을 수행해서는 안 됩니다 (이것은 멤버에 모두 대문자로 구성된 이름을 사용하는 또 하나의 이유입니다):

>>> class FieldTypes(Enum):
...     name = 0
...     value = 1
...     size = 2
...
>>> FieldTypes.value.size
<FieldTypes.size: 2>
>>> FieldTypes.size.value
2

버전 3.5에서 변경.

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

Enum 형(가령 int, str 등)과 혼합된 Enum 멤버는 혼합형의 규칙에 따라 평가됩니다; 그렇지 않으면, 모든 멤버가 True로 평가됩니다. 여러분 자신의 Enum의 불리언 평가를 멤버의 값에 따르게 하려면 클래스에 다음을 추가하십시오:

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

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

메서드가 있는 Enum 클래스

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

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

Flag 멤버를 결합하기

Flag 멤버 조합의 이름이 지정되지 않으면, repr()은 모든 이름 지정된 플래그와 값에 있는 플래그의 모든 이름 지정된 조합을 포함합니다:

>>> 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.CYAN|MAGENTA|BLUE|YELLOW|GREEN|RED: 7>