파이썬에서 'Enum'을 어떻게 나타낼 수 있습니까?


1143

저는 주로 C # 개발자이지만 현재 Python 프로젝트를 진행하고 있습니다.

파이썬에서 Enum과 동등한 것을 어떻게 나타낼 수 있습니까?

답변:


2688

PEP 435에 설명 된대로 열거 형이 Python 3.4에 추가되었습니다 . 또한 pypi 에서 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 및 2.4백 포트 되었습니다 .

고급 Enum 기술을 사용하려면 aenum 라이브러리 (2.7, 3.3 이상,.와 동일한 작성자)를 사용해보십시오 enum34. 코드는 py2와 py3 사이에서 완벽하게 호환되지 않습니다 (예 : __order__python 2에 필요 ).

  • 사용하려면 enum34수행$ pip install enum34
  • 사용하려면 aenum수행$ pip install aenum

설치 enum(아무 번호는) 완전히 다른 호환되지 않는 버전을 설치하지 않습니다.


from enum import Enum     # for enum34, or the stdlib version
# from aenum import Enum  # for the aenum version
Animal = Enum('Animal', 'ant bee cat dog')

Animal.ant  # returns <Animal.ant: 1>
Animal['ant']  # returns <Animal.ant: 1> (string lookup)
Animal.ant.name  # returns 'ant' (inverse lookup)

또는 동등하게 :

class Animal(Enum):
    ant = 1
    bee = 2
    cat = 3
    dog = 4

이전 버전에서 열거 형을 달성하는 한 가지 방법은 다음과 같습니다.

def enum(**enums):
    return type('Enum', (), enums)

다음과 같이 사용됩니다.

>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'

다음과 같은 방법으로 자동 열거를 쉽게 지원할 수도 있습니다.

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    return type('Enum', (), enums)

다음과 같이 사용됩니다.

>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1

값을 다시 이름으로 변환하는 지원은 다음과 같이 추가 할 수 있습니다.

def enum(*sequential, **named):
    enums = dict(zip(sequential, range(len(sequential))), **named)
    reverse = dict((value, key) for key, value in enums.iteritems())
    enums['reverse_mapping'] = reverse
    return type('Enum', (), enums)

이것은 해당 이름을 가진 모든 것을 덮어 쓰지만, 열거 형을 출력으로 렌더링하는 데 유용합니다. 역 매핑이 존재하지 않으면 KeyError가 발생합니다. 첫 번째 예를 들면 다음과 같습니다.

>>> Numbers.reverse_mapping['three']
'THREE'

1
이해할 수 없었습니다. 왜 enum (* sequential, ** named) 메소드에서 kwargs (** named)를 전달 했습니까? 설명 해주세요. kwargs가 없으면 작동합니다. 나는 그것을 확인했다.
Seenu S

열거 (이름, 값)의 파이썬 3의 기능 API와 호환되도록 파이썬이 기능을 업데이트 할 수 좋을 것이다
bscan

**named이전 버전의 enum 함수에서 var kwargs ( )는 다음과 같은 사용자 정의 값을 지원합니다.enum("blue", "red", "green", black=0)
Éric Araujo

823

PEP 435 이전에는 Python에는 동등한 기능이 없었지만 직접 구현할 수있었습니다.

나 자신, 나는 이것을 간단하게 유지하는 것을 좋아한다.

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Python 3.4 ( PEP 435 )에서 Enum 을 기본 클래스로 만들 수 있습니다 . 이것은 PEP에 설명 된 약간의 추가 기능을 제공합니다. 예를 들어 열거 형 멤버는 정수와 구별되며 a name와 a 로 구성 value됩니다.

class Animal(Enum):
    DOG = 1
    CAT = 2

print(Animal.DOG)
# <Animal.DOG: 1>

print(Animal.DOG.value)
# 1

print(Animal.DOG.name)
# "DOG"

값을 입력하지 않으려면 다음 바로 가기를 사용하십시오.

class Animal(Enum):
    DOG, CAT = range(2)

Enum구현 은리스트로 변환 될 수 있고 반복 가능하다 . 멤버의 순서는 선언 순서이며 값과 관련이 없습니다. 예를 들면 다음과 같습니다.

class Animal(Enum):
    DOG = 1
    CAT = 2
    COW = 0

list(Animal)
# [<Animal.DOG: 1>, <Animal.CAT: 2>, <Animal.COW: 0>]

[animal.value for animal in Animal]
# [1, 2, 0]

Animal.CAT in Animal
# True

51
아니요, 클래스 변수입니다.
Georg Schölly 2016 년

246
파이썬은 기본적으로 동적입니다. 파이썬과 같은 언어, 특히없는 경우 컴파일 타임 안전을 강제 할 정당한 이유는 없습니다. 그리고 또 다른 것은 ... 좋은 패턴은 그것이 만들어진 맥락에서만 좋습니다. 사용중인 도구에 따라 좋은 패턴을 대체하거나 완전히 쓸모가 없을 수도 있습니다.
Alexandru Nedelcu

20
@Longpoke 값이 100 인 경우 분명히 잘못된 일을하고 있습니다.) 열거 형과 관련된 숫자를 좋아합니다 ... 문자열과 문자열로 작성하기 쉽고 데이터베이스에 쉽게 유지할 수 있으며 호환됩니다. 쉽게 마샬링 할 수있는 C / C ++ 열거 형
Alexandru Nedelcu

50
나는 이것을 숫자로 대체하여 사용 object()합니다.
Tobu

9
원래 PEP354는 더 이상 거부되지 않고 이제 대체 됨으로 표시됩니다. PEP435는 Python 3.4 용 표준 Enum을 추가합니다. python.org/dev/peps/pep-0435
Peter Hansen

322

다음은 하나의 구현입니다.

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError

사용법은 다음과 같습니다.

Animals = Enum(["DOG", "CAT", "HORSE"])

print(Animals.DOG)

51
우수한. 이것은 더 오버라이드 (override)에 의해 개선 될 수 __setattr__(self, name, value)어쩌면 __delattr__(self, name)당신이 실수로 작성하는 경우 그래서 Animals.DOG = CAT, 그것은 자동으로 실패합니다.
Joonas Pulakka

15
@shahjapan : 흥미롭지 만 상대적으로 느리다 : 각 액세스에 대해 테스트가 수행된다 Animals.DOG. 또한, constats의 값은 문자열이므로 이러한 상수와의 비교는 정수가 값으로 허용되는 경우보다 느립니다.
Eric O Lebigot

3
@ shahjapan : 나는이 솔루션이 Alexandru 나 Mark의 짧은 솔루션만큼 읽기 쉽지 않다고 주장한다. 그러나 흥미로운 솔루션입니다. :)
Eric O Lebigot

오버라이드 방법 대신 메소드 setattr()내부의 함수를 사용해 보았습니다 . 나는 이것이 같은 방식으로 작동한다고 가정합니다. AttributeError__init__()__getattr__()
Harshith JV 2011

8
@ AndréTerra : try-except블록 멤버십 설정을 어떻게 확인 합니까?
bgusach

210

숫자 값이 필요한 경우 가장 빠른 방법은 다음과 같습니다.

dog, cat, rabbit = range(3)

Python 3.x에서는 끝에 별표 표시된 자리 표시자를 추가 할 수 있습니다. 메모리를 낭비하지 않고 계산할 수없는 경우 나머지 범위 값을 모두 흡수합니다.

dog, cat, rabbit, horse, *_ = range(100)

1
그러나 이것은 더 많은 메모리가 필요할 수 있습니다!
MJ

파이썬이 개봉 할 값의 수를 확인한다는 점을 감안할 때 별표 표시된 자리 표시 자의 요점을 보지 못합니다 (따라서 계산합니다).
Gabriel Devillers

@GabrielDevillers, 할당 할 튜플의 요소 수에 불일치가 있으면 Python에서 예외가 발생한다고 생각합니다.
마크 해리슨

1
실제로, 그것은 내 테스트 (Python2,3)에서하지만 프로그래머가 계산하는 실수는 첫 번째 테스트에서 잡히는 것을 의미합니다 (메시지가 정확한 메시지를 표시 함).
Gabriel Devillers

1
셀 수 없습니다. 별표 표시된 자리 표시 자도 내 재정을 수정할 수 있습니까?
javadba

131

당신을위한 최선의 해결책은 당신이 가짜 에서 요구하는 것에 달려 있습니다 enum.

간단한 열거 형 :

다른 항목을 식별하는 이름enum 목록 만 필요한 경우 Mark Harrison 의 솔루션 (위)이 훌륭합니다.

Pen, Pencil, Eraser = range(0, 3)

를 사용하면 시작 값range 을 설정할 수 있습니다 .

Pen, Pencil, Eraser = range(9, 12)

위의 항목 외에도 항목이 일종의 컨테이너 에 속 해야하는 경우 클래스에 포함하십시오.

class Stationery:
    Pen, Pencil, Eraser = range(0, 3)

열거 형 항목을 사용하려면 컨테이너 이름과 항목 이름을 사용해야합니다.

stype = Stationery.Pen

복잡한 열거 형 :

열거 형의 열거 목록이나 열거 형의보다 복잡한 사용의 경우 이러한 솔루션으로는 충분하지 않습니다. Python Cookbook에 게시 된 Python 에서 열거 시뮬레이션을 위한 Will Ware의 레시피를 확인할 수 있습니다. 온라인 버전은 여기에서 구할 수 있습니다 .

더 많은 정보:

PEP 354 : Python의 열거에는 Python의 열거에 대한 흥미로운 제안과 거부 이유가 있습니다.


7
range가 0 인 경우 첫 번째 인수를 생략 할 수 있습니다
ToonAlfrink

어떤 목적에 맞는 또 다른 가짜 열거 형은 my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))입니다. 그런 다음 my_enum조회에 사용될 수 있습니다. 예를 들어 my_enum['Item0']시퀀스에 대한 색인 일 수 있습니다. str.split중복이있는 경우 예외를 발생시키는 함수로 결과를 랩핑 할 수 있습니다 .
아나 님 버스

좋은! 플래그에 대 한Flag1, Flag2, Flag3 = [2**i for i in range(3)]
majkelx

이것은 최선의 답변입니다
YURIY Pozniak

78

Java JDK 5 이전에 사용 된 typesafe 열거 형 패턴에는 여러 가지 장점이 있습니다. Alexandru의 답변과 마찬가지로 클래스를 만들고 클래스 수준 필드는 열거 형 값입니다. 그러나 열거 형 값은 작은 정수가 아닌 클래스의 인스턴스입니다. 이것은 열거 형 값이 실수로 작은 정수와 동등하게 비교되지 않는다는 이점이 있습니다. 인쇄 방법을 제어하고, 유용한 경우 임의의 방법을 추가하고 isinstance를 사용하여 어설 션을 만들 수 있습니다.

class Animal:
   def __init__(self, name):
       self.name = name

   def __str__(self):
       return self.name

   def __repr__(self):
       return "<Animal: %s>" % self

Animal.DOG = Animal("dog")
Animal.CAT = Animal("cat")

>>> x = Animal.DOG
>>> x
<Animal: dog>
>>> x == 1
False

python-dev 의 최근 스레드는 다음을 포함하여 야생에 두 개의 열거 형 라이브러리가 있다고 지적했습니다.


16
나는 이것이 매우 나쁜 접근법이라고 생각합니다. Animal.DOG = Animal ( "dog") Animal.DOG2 = Animal ( "dog") assert Animal.DOG == Animal.DOG2 실패 ...
혼란

11
@Confusion 사용자는 생성자를 호출해서는 안되며, 생성자조차도 구현 세부 사항이므로 새로운 열거 값을 만드는 것은 의미가 없으며 종료 코드는 코드를 사용하지 않는 코드를 사용하는 사람과 통신해야한다는 사실 "올바른 일을하십시오". 물론 Animal.from_name ( "dog")-> Animal.DOG를 구현하지 않아도됩니다.
Aaron Maenpaa 2009

13
"당신의 enum 값이 실수로 작은 정수와 동등하지 않다는 장점"이것의 장점은 무엇입니까? 열거 형과 정수를 비교하는 데 어떤 문제가 있습니까? 특히 열거 형을 데이터베이스에 저장하는 경우 일반적으로 정수로 저장하기를 원하므로 언젠가는 정수와 비교해야합니다.
ibz

3
@ 아론 마엔 파. 옳은. 그것은 여전히 ​​부서지고 지나치게 복잡한 방법입니다.
aaronasterling

4
@AaronMcSmooth 이것은 실제로 "Enums는 단지 몇 개의 정수의 이름"이라는 C 관점에서 오는지 또는 열거 형 값이 실제 객체이고 메소드가있는 더 객체 지향적 접근 방식에 의존하는지 여부에 달려 있습니다 1.5, 그리고 타입 안전 열거 형 패턴이 가고 있습니다). 개인적으로 switch 문을 좋아하지 않으므로 실제 객체 인 열거 형 값을 기대합니다.
Aaron Maenpaa

61

Enum 클래스는 하나의 라이너가 될 수 있습니다.

class Enum(tuple): __getattr__ = tuple.index

사용 방법 (정방향 및 역방향 조회, 키, 값, 항목 등)

>>> State = Enum(['Unclaimed', 'Claimed'])
>>> State.Claimed
1
>>> State[1]
'Claimed'
>>> State
('Unclaimed', 'Claimed')
>>> range(len(State))
[0, 1]
>>> [(k, State[k]) for k in range(len(State))]
[(0, 'Unclaimed'), (1, 'Claimed')]
>>> [(k, getattr(State, k)) for k in State]
[('Unclaimed', 0), ('Claimed', 1)]

나는 그것이 가장 간단한 최종 가장 우아한 솔루션이라고 생각합니다. 파이썬 2.4 (예, 기존 레거시 서버) 튜플은 색인을 생성하지 않았습니다. 나는 목록으로 바꾸는 것을 해결했다.
Massimo

나는 Jupyter 노트북에서 이것을 시도해 한 줄 정의로는 작동하지 않지만 getattr 정의를 두 번째 (들여진) 줄에 넣는 것이 허용 된다는 것을 발견했습니다 .
user5920660

이 솔루션을 사용하면 in키워드를 사용하여 깔끔한 멤버를 검색 할 수 있습니다. 사용법 :'Claimed' in Enum(['Unclaimed', 'Claimed'])
Farzad Abdolhosseini

51

그래서 동의합니다. 파이썬에서 타입 안전을 강요하지는 않지만 어리석은 실수로부터 자신을 보호하고 싶습니다. 그래서 우리는 이것에 대해 어떻게 생각합니까?

class Animal(object):
    values = ['Horse','Dog','Cat']

    class __metaclass__(type):
        def __getattr__(self, name):
            return self.values.index(name)

열거 형을 정의 할 때 가치 충돌을 피할 수 있습니다.

>>> Animal.Cat
2

또 다른 편리한 장점이 있습니다 : 정말 빠른 역방향 조회 :

def name_of(self, i):
    return self.values[i]

나는 이것을 좋아하지만 튜플의 효율성을 위해 값을 잠글 수도 있습니까? 나는 그것을 가지고 놀았고 init 에서 args의 self.values를 설정하는 버전을 생각해 냈습니다 . 선언 할 수있는 것이 좋습니다 Animal = Enum('horse', 'dog', 'cat'). 또한 self.values에 누락 된 항목이있는 경우 getattr 에서 ValueError를 잡습니다 . 대신 제공된 이름 문자열로 AttributeError를 발생시키는 것이 좋습니다. 해당 영역에 대한 제한된 지식을 기반으로 Python 2.7에서 메타 클래스를 작동시킬 수 없었지만 사용자 정의 Enum 클래스는 직선 인스턴스 메소드에서 잘 작동합니다.
trojjer

49

파이썬에는에 상응하는 빌트인이 없으며 enum다른 답변에는 자체 구현 아이디어가 있습니다 ( 파이썬 요리 책 의 최상위 버전 에 관심이있을 수도 있습니다 ).

그러나 enumC에서 호출이 필요한 상황에서는 일반적으로 간단한 문자열을 사용합니다 . 객체 / 속성이 구현되는 방식 때문에 (C) Python은 짧은 문자열로 매우 빠르게 작동하도록 최적화되어 있으므로 정수를 사용하면 실제로 성능상의 이점이 아닙니다. 오타 / 유효하지 않은 값으로부터 보호하기 위해 선택한 위치에 검사를 삽입 할 수 있습니다.

ANIMALS = ['cat', 'dog', 'python']

def take_for_a_walk(animal):
    assert animal in ANIMALS
    ...

(클래스를 사용하는 것에 비해 한 가지 단점은 자동 완성의 이점을 잃어 버린다는 것입니다)


2
이 솔루션을 선호합니다. 가능한 경우 내장 유형을 사용하고 싶습니다.
Seun Osewa

그 버전은 실제로 최고가 아닙니다. 많은 테스트 코드가 제공됩니다
Casebash

1
실제로 "올바른"버전은 주석에 있으며 훨씬 더 복잡합니다. 기본 버전에는 사소한 버그가 있습니다.
Casebash

39

2013 년 5 월 10 일, Guido는 PEP 435 를 Python 3.4 표준 라이브러리 에 수용하기로 동의했습니다 . 이것은 파이썬이 마침내 열거를 기본적으로 지원한다는 것을 의미합니다!

Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 및 2.4에 사용할 수있는 백 포트가 있습니다. enum34로 Pypi에 있습니다.

선언:

>>> from enum import Enum
>>> class Color(Enum):
...     red = 1
...     green = 2
...     blue = 3

대표:

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

되풀이:

>>> for color in Color:
...   print(color)
...
Color.red
Color.green
Color.blue

프로그래밍 방식의 액세스 :

>>> Color(1)
Color.red
>>> Color['blue']
Color.blue

자세한 내용 은 제안서를 참조하십시오 . 공식 문서가 곧 나올 것입니다.


33

파이썬에서 열거 형을 다음과 같이 정의하는 것을 선호합니다.

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

정수가 고유하다는 것을 걱정할 필요가 없기 때문에 정수를 사용하는 것보다 버그가 방지됩니다 (예 : Dog = 1 및 Cat = 1이라고 말하면 망할 것입니다).

오타에 대해 걱정할 필요가 없으므로 문자열을 사용하는 것보다 버그가 더 적습니다 (예 : x == "catt"는 자동으로 실패하지만 x == Animal.Catt는 런타임 예외입니다).


31
def M_add_class_attribs(attribs):
    def foo(name, bases, dict_):
        for v, k in attribs:
            dict_[k] = v
        return type(name, bases, dict_)
    return foo

def enum(*names):
    class Foo(object):
        __metaclass__ = M_add_class_attribs(enumerate(names))
        def __setattr__(self, name, value):  # this makes it read-only
            raise NotImplementedError
    return Foo()

다음과 같이 사용하십시오.

Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError

고유 한 기호 만 원하고 값에 신경 쓰지 않으면이 줄을 바꾸십시오.

__metaclass__ = M_add_class_attribs(enumerate(names))

이것으로 :

__metaclass__ = M_add_class_attribs((object(), name) for name in names)

11
당신이 변경되었을 경우 이럴 그것은 청소기 될 enum(names)하기 enum(*names)를 호출 할 때 다음 여분의 괄호를 드롭 할 수 -.
Chris Lutz

나는이 접근법을 좋아한다. 실제로 속성 값을 이름과 동일한 문자열로 설정하여 Animal.DOG == 'DOG'라는 멋진 속성을 가지므로 자동으로 문자열을 지정합니다. (디버그 출력을 인쇄하기 위해 대단히 도움이됩니다.)
테드 Mielczarek

23

파이썬 3.4부터 열거 형에 대한 공식적인 지원이있을 것입니다. Python 3.4 documentation page에서 문서와 예제를 찾을 수 있습니다 .

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

from enum import Enum
class Color(Enum):
     red = 1
     green = 2
     blue = 3

후면 포팅도 지원됩니다. 이것은 갈 길입니다.
srock

22

흠 ... 열거 형에 가장 가까운 것은 다음과 같이 정의 된 사전이라고 생각합니다.

months = {
    'January': 1,
    'February': 2,
    ...
}

또는

months = dict(
    January=1,
    February=2,
    ...
)

그런 다음 상수의 기호 이름을 다음과 같이 사용할 수 있습니다.

mymonth = months['January']

튜플 목록 또는 튜플 튜플과 같은 다른 옵션이 있지만 사전은 값에 액세스 할 수있는 "기호"(일정한 문자열) 방법을 제공하는 유일한 옵션입니다.

편집 : 나는 Alexandru의 대답도 좋아합니다!


그리고 무엇보다도 문자열 값이 콤보 상자 항목으로 표시되어야하는 것처럼 값에 액세스해야하는 경우 사전에서 쉽게 반복 할 수 있습니다. 따라서 대신 사전을 대신하여 사전을 사용하십시오.
LEMUEL ADANE

22

다음을 사용하여 파이썬에서 열거 형을 구현하는 매우 간단한 또 ​​다른 방법은 namedtuple다음과 같습니다.

from collections import namedtuple

def enum(*keys):
    return namedtuple('Enum', keys)(*keys)

MyEnum = enum('FOO', 'BAR', 'BAZ')

또는 대안 적으로

# With sequential number values
def enum(*keys):
    return namedtuple('Enum', keys)(*range(len(keys)))

# From a dict / keyword args
def enum(**kwargs):
    return namedtuple('Enum', kwargs.keys())(*kwargs.values())

하위 클래스 위의 메소드와 마찬가지로 set다음을 허용합니다.

'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO

그러나 다른 키와 값을 가질 수 있으므로 더 많은 유연성이 있습니다. 이것은 허용

MyEnum.FOO < MyEnum.BAR

순차적 인 숫자 값을 채우는 버전을 사용하는 경우 예상대로 작동합니다.


20

내가 사용하는 것 :

class Enum(object):
    def __init__(self, names, separator=None):
        self.names = names.split(separator)
        for value, name in enumerate(self.names):
            setattr(self, name.upper(), value)
    def tuples(self):
        return tuple(enumerate(self.names))

사용하는 방법:

>>> state = Enum('draft published retracted')
>>> state.DRAFT
0
>>> state.RETRACTED
2
>>> state.FOO
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: 'Enum' object has no attribute 'FOO'
>>> state.tuples()
((0, 'draft'), (1, 'published'), (2, 'retracted'))

따라서 이것은 장고 모델에서 선택으로 사용할 state.PUBLISHED 및 두 개의 튜플과 같은 정수 상수를 제공합니다.


17

davidg는 dicts 사용을 권장합니다. 한 걸음 더 나아가 세트를 사용합니다.

months = set('January', 'February', ..., 'December')

이제 값이 다음과 같이 세트의 값 중 하나와 일치하는지 테스트 할 수 있습니다.

if m in months:

dF와 마찬가지로, 나는 보통 열거 형 대신 문자열 상수를 사용합니다.


당신이 set을 상속하고 getattr 메소드를 제공한다면 훨씬 더 좋습니다 !
shahjapan

17

간단하게 유지하십시오.

class Enum(object): 
    def __init__(self, tupleList):
            self.tupleList = tupleList

    def __getattr__(self, name):
            return self.tupleList.index(name)

그때:

DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1

16

이것은 내가 본 것 중 가장 좋은 것입니다 : "파이썬의 퍼스트 클래스 열거 형"

http://code.activestate.com/recipes/413486/

그것은 당신에게 클래스를 제공하고 클래스는 모든 열거 형을 포함합니다. 열거 형은 서로 비교할 수 있지만 특별한 가치는 없습니다. 정수 값으로 사용할 수 없습니다. (정수 값인 C 열거 형에 익숙하기 때문에 처음에는 저항했습니다.하지만 정수로 사용할 수 없으면 실수로 정수로 사용할 수 없으므로 전반적으로 승리라고 생각합니다 .) 각 열거 형은 고유 한 값입니다. 열거 형을 인쇄하고 반복 할 수 있으며 열거 형 값이 열거 형에 있는지 테스트 할 수 있습니다. 꽤 완전하고 매끄 럽습니다.

편집 (cfi) : 위의 링크는 Python 3과 호환되지 않습니다. 다음은 파이썬 3에 대한 enum.py 포트입니다.

def cmp(a,b):
   if a < b: return -1
   if b < a: return 1
   return 0


def Enum(*names):
   ##assert names, "Empty enums are not supported" # <- Don't like empty enums? Uncomment!

   class EnumClass(object):
      __slots__ = names
      def __iter__(self):        return iter(constants)
      def __len__(self):         return len(constants)
      def __getitem__(self, i):  return constants[i]
      def __repr__(self):        return 'Enum' + str(names)
      def __str__(self):         return 'enum ' + str(constants)

   class EnumValue(object):
      __slots__ = ('__value')
      def __init__(self, value): self.__value = value
      Value = property(lambda self: self.__value)
      EnumType = property(lambda self: EnumType)
      def __hash__(self):        return hash(self.__value)
      def __cmp__(self, other):
         # C fans might want to remove the following assertion
         # to make all enums comparable by ordinal value {;))
         assert self.EnumType is other.EnumType, "Only values from the same enum are comparable"
         return cmp(self.__value, other.__value)
      def __lt__(self, other):   return self.__cmp__(other) < 0
      def __eq__(self, other):   return self.__cmp__(other) == 0
      def __invert__(self):      return constants[maximum - self.__value]
      def __nonzero__(self):     return bool(self.__value)
      def __repr__(self):        return str(names[self.__value])

   maximum = len(names) - 1
   constants = [None] * len(names)
   for i, each in enumerate(names):
      val = EnumValue(i)
      setattr(EnumClass, each, val)
      constants[i] = val
   constants = tuple(constants)
   EnumType = EnumClass()
   return EnumType


if __name__ == '__main__':
   print( '\n*** Enum Demo ***')
   print( '--- Days of week ---')
   Days = Enum('Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su')
   print( Days)
   print( Days.Mo)
   print( Days.Fr)
   print( Days.Mo < Days.Fr)
   print( list(Days))
   for each in Days:
      print( 'Day:', each)
   print( '--- Yes/No ---')
   Confirmation = Enum('No', 'Yes')
   answer = Confirmation.No
   print( 'Your answer is not', ~answer)

이 레시피는 거부 된 PEP의 기초로 사용되었습니다. python.org/dev/peps/pep-0354 내가 좋아하는 확장 : 열거 형 값에는 내부 정수 값을 가져올 수있는 멤버 변수가 있어야합니다. 실수로 열거 형을 정수로 캐스트 할 수 없으므로 .__int__()메소드는 열거 형에 대한 예외를 제기해야합니다. 그러나 가치를 얻는 방법이 있어야합니다. 클래스 정의 시간에 특정 정수 값을 설정할 수 있으므로 stat모듈 의 상수와 같은 것에 열거 형을 사용할 수 있습니다 .
steveha

14

이진 파일 형식을 디코딩하기 위해 Enum 클래스가 필요할 때가 있습니다. 내가 원했던 기능은 간결한 열거 형 정의, 정수 값 또는 문자열로 열거 형 인스턴스를 자유롭게 만들 수있는 기능 및 유용한 repr평가입니다. 내가 끝내었던 것은 다음과 같습니다.

>>> class Enum(int):
...     def __new__(cls, value):
...         if isinstance(value, str):
...             return getattr(cls, value)
...         elif isinstance(value, int):
...             return cls.__index[value]
...     def __str__(self): return self.__name
...     def __repr__(self): return "%s.%s" % (type(self).__name__, self.__name)
...     class __metaclass__(type):
...         def __new__(mcls, name, bases, attrs):
...             attrs['__slots__'] = ['_Enum__name']
...             cls = type.__new__(mcls, name, bases, attrs)
...             cls._Enum__index = _index = {}
...             for base in reversed(bases):
...                 if hasattr(base, '_Enum__index'):
...                     _index.update(base._Enum__index)
...             # create all of the instances of the new class
...             for attr in attrs.keys():
...                 value = attrs[attr]
...                 if isinstance(value, int):
...                     evalue = int.__new__(cls, value)
...                     evalue._Enum__name = attr
...                     _index[value] = evalue
...                     setattr(cls, attr, evalue)
...             return cls
... 

그것을 사용하는 기발한 예 :

>>> class Citrus(Enum):
...     Lemon = 1
...     Lime = 2
... 
>>> Citrus.Lemon
Citrus.Lemon
>>> 
>>> Citrus(1)
Citrus.Lemon
>>> Citrus(5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __new__
KeyError: 5
>>> class Fruit(Citrus):
...     Apple = 3
...     Banana = 4
... 
>>> Fruit.Apple
Fruit.Apple
>>> Fruit.Lemon
Citrus.Lemon
>>> Fruit(1)
Citrus.Lemon
>>> Fruit(3)
Fruit.Apple
>>> "%d %s %r" % ((Fruit.Apple,)*3)
'3 Apple Fruit.Apple'
>>> Fruit(1) is Citrus.Lemon
True

주요 특징들:

  • str(), int()repr()모든 생산 가장 유용 가능한 출력은 enumartion 각각의 이름, 그 정수 값을 평가하는 것은 열거을 맞댄 것을 파이썬 식.
  • 생성자가 반환 한 열거 형 값은 실수로 열거 형 값이 아닌 미리 정의 된 값으로 제한됩니다.
  • 열거 된 값은 싱글 톤입니다. 그들은 엄격하게 비교 될 수 있습니다is

열거 형을 쉽게 정의 할 수 있도록 자체 메타 클래스와 함께 수퍼 클래스를 사용하는 것이 정말 좋습니다. 여기서 누락 된 것은 __contains__ 메소드입니다. 주어진 변수가 열거 형의 일부인지 확인할 수 있기를 원합니다. 주로 함수 매개 변수의 허용 가능한 값에 대한 열거 형을 원하기 때문입니다.
xorsyst

이것은 실제로 원래 생성 한 버전의 약간 손질 된 버전입니다 (여기에서 찾을 수 있습니다 : enum_strict.py ) v __instancecheck__메소드 를 정의합니다 . 클래스는 인스턴스의 모음이 아니므 1 in Fruit로 터무니 없습니다. 그러나 링크 된 버전은 isinstance(1, Fruit)클래스와 인스턴스의 개념에서 더 정확한 것을 지원 합니다.
SingleNegationElimination

그러나 클래스를 잊어 버리고 열거 형으로 생각하면 클래스로 생각하는 것이 합리적입니다. 예를 들어 파일 열기 모드 (MODE.OPEN, MODE.WRITE 등)가있을 수 있습니다. 함수의 매개 변수를 확인하고 싶습니다. MODE의 mode : isintance (mode, Mode)
xorsyst

난 그냥 int 치의보다 더 많은 지원과 매우 유사 무언가를 올려 있고, 문서화 및 테스트, GitHub의에 : github.com/hmeine/named_constants
hans_meine

12

Python의 새로운 표준은 PEP 435 이므로 Enum 클래스는 다음 버전의 Python에서 사용할 수 있습니다.

>>> from enum import Enum

그러나 지금 사용하기 시작 하면 PEP에 동기를 부여한 원본 라이브러리 를 설치할 수 있습니다 .

$ pip install flufl.enum

그런 다음 온라인 가이드에 따라 사용할 수 있습니다 .

>>> from flufl.enum import Enum
>>> class Colors(Enum):
...     red = 1
...     green = 2
...     blue = 3
>>> for color in Colors: print color
Colors.red
Colors.green
Colors.blue

10
def enum(*sequential, **named):
    enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
    return type('Enum', (), enums)

이름을 지정하면 문제가되지만 값 대신 객체를 만들지 않으면 다음과 같이 할 수 있습니다.

>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False

여기에있는 다른 구현을 사용할 때 (또한 예제에서 명명 된 인스턴스를 사용할 때) 다른 열거 형의 객체를 비교하려고 시도하지 않아야합니다. 여기에 가능한 함정이 있습니다.

>>> DOG = enum('BARK'=1, 'WALK'=2, 'SIT'=3)
>>> CAT = enum('WALK'=1, 'SIT'=2)
>>> pet1_state = DOG.BARK
>>> pet2_state = CAT.WALK
>>> pet1_state == pet2_state
True

이케!


9

나는 Alec Thomas의 솔루션 (http://stackoverflow.com/a/1695250)을 정말 좋아합니다.

def enum(**enums):
    '''simple constant "enums"'''
    return type('Enum', (object,), enums)

우아하고 깔끔하게 보이지만 지정된 속성으로 클래스를 만드는 함수 일뿐입니다.

함수를 약간 수정하면 좀 더 '열거'하도록 할 수 있습니다.

참고 : pygtk의 새로운 스타일 'enum'의 동작을 재현하여 다음 예제를 만들었습니다 (Gtk.MessageType.WARNING).

def enum_base(t, **enums):
    '''enums with a base class'''
    T = type('Enum', (t,), {})
    for key,val in enums.items():
        setattr(T, key, T(val))

    return T

지정된 형식을 기준으로 열거 형을 만듭니다. 이전 함수와 같은 속성 액세스를 제공하는 것 외에도 유형과 관련하여 Enum을 기대하는 것처럼 작동합니다. 또한 기본 클래스를 상속합니다.

예를 들어, 정수 열거 형 :

>>> Numbers = enum_base(int, ONE=1, TWO=2, THREE=3)
>>> Numbers.ONE
1
>>> x = Numbers.TWO
>>> 10 + x
12
>>> type(Numbers)
<type 'type'>
>>> type(Numbers.ONE)
<class 'Enum'>
>>> isinstance(x, Numbers)
True

이 방법으로 수행 할 수있는 또 다른 흥미로운 점은 내장 방법을 재정 의하여 특정 동작을 사용자 정의하는 것입니다.

def enum_repr(t, **enums):
    '''enums with a base class and repr() output'''
    class Enum(t):
        def __repr__(self):
            return '<enum {0} of type Enum({1})>'.format(self._name, t.__name__)

    for key,val in enums.items():
        i = Enum(val)
        i._name = key
        setattr(Enum, key, i)

    return Enum



>>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3)
>>> repr(Numbers.ONE)
'<enum ONE of type Enum(int)>'
>>> str(Numbers.ONE)
'1'

이 "기본"유형 아이디어는 단정합니다 :)
MestreLion

예, 새로운 Python 3.4 Enum으로도 가능합니다 : python.org/dev/peps/pep-0435/#other-derived-enumerations
bj0

7

PyPI 의 enum 패키지 는 강력한 enum 구현을 제공합니다. 이전 답변은 PEP 354를 언급했습니다. 이것은 거부되었지만 제안은 http://pypi.python.org/pypi/enum 으로 구현되었습니다 .

사용법이 쉽고 우아합니다.

>>> from enum import Enum
>>> Colors = Enum('red', 'blue', 'green')
>>> shirt_color = Colors.green
>>> shirt_color = Colors[2]
>>> shirt_color > Colors.red
True
>>> shirt_color.index
2
>>> str(shirt_color)
'green'

5

열거 형에 클래스 상수를 사용한다는 Alexandru의 제안은 잘 작동합니다.

또한 사람이 읽을 수있는 문자열 표현을 찾기 위해 각 상수 집합에 대한 사전을 추가하고 싶습니다.

이것은 두 가지 목적을 제공합니다.

class Animal:    
  TYPE_DOG = 1
  TYPE_CAT = 2

  type2str = {
    TYPE_DOG: "dog",
    TYPE_CAT: "cat"
  }

  def __init__(self, type_):
    assert type_ in self.type2str.keys()
    self._type = type_

  def __repr__(self):
    return "<%s type=%s>" % (
        self.__class__.__name__, self.type2str[self._type].upper())

5

다음은 내가 찾은 몇 가지 다른 특성을 가진 접근법입니다.

  • 어휘 순서가 아닌 열거 형 순서를 기반으로> 및 <비교 허용
  • xa, x [ 'a'] 또는 x [0]으로 이름, 속성 또는 색인으로 항목을 지정할 수 있습니다.
  • [:] 또는 [-1]과 같은 슬라이싱 작업을 지원합니다

가장 중요한 것은 다른 유형의 열거 형 간의 비교를 방지하는 것입니다 !

http://code.activestate.com/recipes/413486-first-class-enums-in-python 기반으로합니다 .

이 접근 방식과 다른 점을 설명하기 위해 많은 doctest가 여기에 포함되었습니다.

def enum(*names):
    """
SYNOPSIS
    Well-behaved enumerated type, easier than creating custom classes

DESCRIPTION
    Create a custom type that implements an enumeration.  Similar in concept
    to a C enum but with some additional capabilities and protections.  See
    http://code.activestate.com/recipes/413486-first-class-enums-in-python/.

PARAMETERS
    names       Ordered list of names.  The order in which names are given
                will be the sort order in the enum type.  Duplicate names
                are not allowed.  Unicode names are mapped to ASCII.

RETURNS
    Object of type enum, with the input names and the enumerated values.

EXAMPLES
    >>> letters = enum('a','e','i','o','u','b','c','y','z')
    >>> letters.a < letters.e
    True

    ## index by property
    >>> letters.a
    a

    ## index by position
    >>> letters[0]
    a

    ## index by name, helpful for bridging string inputs to enum
    >>> letters['a']
    a

    ## sorting by order in the enum() create, not character value
    >>> letters.u < letters.b
    True

    ## normal slicing operations available
    >>> letters[-1]
    z

    ## error since there are not 100 items in enum
    >>> letters[99]
    Traceback (most recent call last):
        ...
    IndexError: tuple index out of range

    ## error since name does not exist in enum
    >>> letters['ggg']
    Traceback (most recent call last):
        ...
    ValueError: tuple.index(x): x not in tuple

    ## enums must be named using valid Python identifiers
    >>> numbers = enum(1,2,3,4)
    Traceback (most recent call last):
        ...
    AssertionError: Enum values must be string or unicode

    >>> a = enum('-a','-b')
    Traceback (most recent call last):
        ...
    TypeError: Error when calling the metaclass bases
        __slots__ must be identifiers

    ## create another enum
    >>> tags = enum('a','b','c')
    >>> tags.a
    a
    >>> letters.a
    a

    ## can't compare values from different enums
    >>> letters.a == tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    >>> letters.a < tags.a
    Traceback (most recent call last):
        ...
    AssertionError: Only values from the same enum are comparable

    ## can't update enum after create
    >>> letters.a = 'x'
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'a' is read-only

    ## can't update enum after create
    >>> del letters.u
    Traceback (most recent call last):
        ...
    AttributeError: 'EnumClass' object attribute 'u' is read-only

    ## can't have non-unique enum values
    >>> x = enum('a','b','c','a')
    Traceback (most recent call last):
        ...
    AssertionError: Enums must not repeat values

    ## can't have zero enum values
    >>> x = enum()
    Traceback (most recent call last):
        ...
    AssertionError: Empty enums are not supported

    ## can't have enum values that look like special function names
    ## since these could collide and lead to non-obvious errors
    >>> x = enum('a','b','c','__cmp__')
    Traceback (most recent call last):
        ...
    AssertionError: Enum values beginning with __ are not supported

LIMITATIONS
    Enum values of unicode type are not preserved, mapped to ASCII instead.

    """
    ## must have at least one enum value
    assert names, 'Empty enums are not supported'
    ## enum values must be strings
    assert len([i for i in names if not isinstance(i, types.StringTypes) and not \
        isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode'
    ## enum values must not collide with special function names
    assert len([i for i in names if i.startswith("__")]) == 0,\
        'Enum values beginning with __ are not supported'
    ## each enum value must be unique from all others
    assert names == uniquify(names), 'Enums must not repeat values'

    class EnumClass(object):
        """ See parent function for explanation """

        __slots__ = names

        def __iter__(self):
            return iter(constants)

        def __len__(self):
            return len(constants)

        def __getitem__(self, i):
            ## this makes xx['name'] possible
            if isinstance(i, types.StringTypes):
                i = names.index(i)
            ## handles the more normal xx[0]
            return constants[i]

        def __repr__(self):
            return 'enum' + str(names)

        def __str__(self):
            return 'enum ' + str(constants)

        def index(self, i):
            return names.index(i)

    class EnumValue(object):
        """ See parent function for explanation """

        __slots__ = ('__value')

        def __init__(self, value):
            self.__value = value

        value = property(lambda self: self.__value)

        enumtype = property(lambda self: enumtype)

        def __hash__(self):
            return hash(self.__value)

        def __cmp__(self, other):
            assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable'
            return cmp(self.value, other.value)

        def __invert__(self):
            return constants[maximum - self.value]

        def __nonzero__(self):
            ## return bool(self.value)
            ## Original code led to bool(x[0])==False, not correct
            return True

        def __repr__(self):
            return str(names[self.value])

    maximum = len(names) - 1
    constants = [None] * len(names)
    for i, each in enumerate(names):
        val = EnumValue(i)
        setattr(EnumClass, each, val)
        constants[i] = val
    constants = tuple(constants)
    enumtype = EnumClass()
    return enumtype

3

다음은 Alec Thomas 솔루션의 변형입니다 .

def enum(*args, **kwargs):
    return type('Enum', (), dict((y, x) for x, y in enumerate(args), **kwargs)) 

x = enum('POOH', 'TIGGER', 'EEYORE', 'ROO', 'PIGLET', 'RABBIT', 'OWL')
assert x.POOH == 0
assert x.TIGGER == 1

3

이 솔루션은 목록으로 정의 된 열거에 대한 클래스를 얻는 간단한 방법입니다 (더 이상 성가신 정수 할당은 없습니다).

enumeration.py :

import new

def create(class_name, names):
    return new.classobj(
        class_name, (object,), dict((y, x) for x, y in enumerate(names))
    )

example.py :

import enumeration

Colors = enumeration.create('Colors', (
    'red',
    'orange',
    'yellow',
    'green',
    'blue',
    'violet',
))

2
이것은 클래스를 만드는 정말 오래된 방법입니다. 단순히 type(class_name, (object,), dict(...))대신 사용 하지 않겠습니까?
terminus

3

원래 열거 제안 인 PEP 354 가 몇 년 전에 기각 되었지만 계속 재개됩니다. 어떤 종류의 열거 형이 3.2에 추가되도록 의도되었지만 3.3으로 되돌아 간 다음 잊어 버렸습니다. 그리고 이제 파이썬 3.4에 포함될 PEP 435가 있습니다. PEP 435의 참조 구현은입니다 flufl.enum.

2013 년 4 월 현재, 사람들이 "무언가"가 무엇인지에 대해 합의 할 수있는 한, 3.4에서 표준 라이브러리에 무언가 를 추가해야한다는 일반적인 합의가 있는 것 같습니다 . 어려운 부분입니다. 2013 년 초 몇 달 동안 여기여기 에서 시작하는 실과 6 개의 다른 실을 참조하십시오.

그 사이에 PyPI, ActiveState 등에는 수많은 새로운 디자인과 구현이 표시되므로 FLUFL 디자인이 마음에 들지 않으면 PyPI 검색을 시도하십시오 .


3

다음을 사용하십시오.

TYPE = {'EAN13':   u'EAN-13',
        'CODE39':  u'Code 39',
        'CODE128': u'Code 128',
        'i25':     u'Interleaved 2 of 5',}

>>> TYPE.items()
[('EAN13', u'EAN-13'), ('i25', u'Interleaved 2 of 5'), ('CODE39', u'Code 39'), ('CODE128', u'Code 128')]
>>> TYPE.keys()
['EAN13', 'i25', 'CODE39', 'CODE128']
>>> TYPE.values()
[u'EAN-13', u'Interleaved 2 of 5', u'Code 39', u'Code 128']

나는 장고 모델 선택에 그것을 사용했고, 그것은 매우 파이썬처럼 보입니다. 실제로 Enum은 아니지만 작업을 수행합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.