Programing

Python에서 '이넘'을 어떻게 대표할 수 있을까?

c10106 2022. 3. 10. 22:08
반응형

Python에서 '이넘'을 어떻게 대표할 수 있을까?

나는 주로 C# 개발자인데, 현재 파이톤에서 프로젝트를 하고 있어.

Python에서 Enum과 같은 값을 나타내는 방법은?

PEP 435에 설명된 대로 에넘이 파이썬 3.4에 추가되었다.또한 피피에 대한 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, 2.4로 역포팅되었다.

보다 고급 Enum 기술을 위해 aenum 라이브러리(2.7, 3.3+)를 사용해 보십시오.enum34. py2와 py3는 코드가 완벽하게 호환되지 않는다. 예를 들어, 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)

이렇게 하면 해당 이름을 가진 모든 항목이 덮어쓰지만 출력에서 Enum을 렌더링하는 데 유용하다.그것은 a를 던질 것이다.KeyError역방향 매핑이 존재하지 않는 경우. 번째 : 째 시시 시시:

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

만약 당신이 MyPy를 사용하고 있다면, "enum"을 표현하는 또 다른 방법은 에 있다.

예를 들면 다음과 같다.

from typing import Literal #python >=3.8
from typing_extensions import Literal #python 2.7, 3.4-3.7


Animal = Literal['ant', 'bee', 'cat', 'dog']

def hello_animal(animal: Animal):
    print(f"hello {animal}")

hello_animal('rock') # error
hello_animal('bee') # passes

PEP 435 이전에 파이썬은 동등한 것을 가지고 있지 않았지만 당신은 자신의 것을 구현할 수 있었다.

나 자신은, 단순하게 유지하는 것을 좋아한다(그물망에서 끔찍하게 복잡한 예를 몇 가지 보았다), 이와 같은 것...

class Animal:
    DOG = 1
    CAT = 2

x = Animal.DOG

Python 3.4(PEP 435)에서는 Enumer를 기본 클래스로 만들 수 있다.이를 통해 PEP에 설명된 추가 기능을 조금 더 활용할 수 있다.예를 들어 열거형 멤버는 정수와 구별되며, 정수로 구성된다.name그리고 avalue.

from enum import Enum

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

다음 중 한 가지 구현:

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

사용법은 다음과 같다.

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

print(Animals.DOG)

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

dog, cat, rabbit = range(3)

Python 3.x에서는 끝에 별표가 붙은 자리 표시자를 추가할 수도 있는데, 메모리 낭비를 개의치 않고 다음 값을 셀 수 없는 경우에 대비하여 범위의 나머지 값을 모두 흡수한다.

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

당신을 위한 최선의 해결책은 당신이 가짜로부터 무엇을 요구하느냐에 달려 있을 것이다.

단순 열거형:

서로 다른 항목을 식별하는 이름 목록만 필요한 경우 Mark Harrison(위)의 솔루션은 다음과 같다.

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

또한 를 사용하면 시작 값을 설정할 수 있다.

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

위의 항목 외에도, 항목이 어떤 종류의 컨테이너에 속하도록 요구하는 경우, 클래스에 포함시키십시오.

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

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

stype = Stationery.Pen

복합 열거형:

긴 열거형 목록이나 보다 복잡한 열거형 사용의 경우, 이러한 해결책은 충분하지 않을 것이다.Python Cookbook에 게재된 Python의 Will Ware의 시뮬레이션 Enumerations를 위한 레시피를 참조할 수 있다.그것의 온라인 버전은 여기에서 이용할 수 있다.

추가 정보:

PEP 354: PythonEnumerations는 Python의 Enumerations에 대한 제안의 흥미로운 세부 사항과 왜 그것이 거절되었는지에 대해 알고 있다.

Java 사전 JDK 5에서 사용된 형식 열거 패턴은 많은 장점을 가지고 있다.알렉산드루의 답변에서와 마찬가지로 클래스를 만들고 클래스 수준 필드는 열거형 값이지만 열거형 값은 작은 정수보다는 클래스의 인스턴스(instance)이다.이것은 열거형 값이 실수로 작은 정수와 비교되지 않고 인쇄 방법을 제어하고 유용할 경우 임의의 방법을 추가하고 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

최근 피톤-dev에 실린에서는 야생에 다음과 같은 열거형 도서관들이 두어 개 있다고 지적했다.

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)]

그래서, 나도 동의해.파이톤에서는 형식적인 안전을 강요하지 말자. 하지만 나는 어리석은 실수로부터 나 자신을 보호하고 싶다.그래서 우리는 이것에 대해 어떻게 생각할까?

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]

Python에는 다음과 같은 기능이 내장되어 있지 않다.enum, 그리고 다른 답변들은 당신 자신의 것을 구현하기 위한 아이디어를 가지고 있다 (Python 요리책의 상위 버전에 관심이 있을 수도 있다).

그러나, 다음과 같은 상황에서enumC에서 호출될 것이다. 나는 보통 단순문자열만 사용한다. 개체/속성이 구현되는 방식 때문에, (C)피톤은 어쨌든 짧은 문자열로 매우 빠르게 작동하도록 최적화되어 있기 때문에 정수를 사용하는 데 성능상 이점이 없다.오타/잘못된 값을 방지하기 위해 선택한 위치에 검사를 삽입하십시오.

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

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

(클래스 사용과 비교했을 때 한 가지 단점은 자동 완성 기능을 상실한다는 것이다.)

2013-05-10년 과이도 의장은 PEP 435를 파이썬 3.4 표준 라이브러리에 수용하기로 합의했다.이것은 파이썬이 마침내 열거에 대한 지원을 내장했다는 것을 의미한다!

Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5, 2.4에 사용할 수 있는 백포트가 있다.피피에 열거 34번지야

선언:

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

자세한 내용은 제안서를 참조하십시오.공식 서류는 아마 곧 뒤따라 올 것이다.

나는 Python에서 다음과 같이 Enum을 정의하고 싶다.

class Animal:
  class Dog: pass
  class Cat: pass

x = Animal.Dog

정수를 사용하는 것보다 더 버그 방지가 강하다. 정수를 고유하게 하는 것에 대해 걱정할 필요가 없기 때문이다(예: Dog = 1과 Cat = 1이라고 말했다면 당신은 망칠 것이다).

오타를 걱정할 필요가 없기 때문에 끈을 사용하는 것보다 더 방충이 된다(예: x == "catt"는 조용히 실패하지만 x == Animal).Catt는 런타임 예외다).


ADDENDUM : Dog and Cat이 올바른 메타클라스를 가진 기호 클래스에서 상속받음으로써 이 솔루션을 향상시킬 수 있다.

class SymbolClass(type):
    def __repr__(self): return self.__qualname__
    def __str__(self): return self.__name__

class Symbol(metaclass=SymbolClass): pass


class Animal:
    class Dog(Symbol): pass
    class Cat(Symbol): pass

그런 다음 이러한 값을 사용하여 사전을 색인화하는 경우, 표현 요청은 사전을 잘 보이게 한다.

>>> mydict = {Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
>>> mydict
{Animal.Dog: 'Wan Wan', Animal.Cat: 'Nyaa'}
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)

Python 3.4로부터 에넘에 대한 공식적인 지원이 있다.문서와 예는 Python 3.4 문서 페이지에서 찾을 수 있다.

열거형은 클래스 구문을 사용하여 생성되므로 읽기 쉽고 쓰기 쉽다.대체 생성 방법은 Functional API에 설명되어 있다.열거형을 정의하려면 다음과 같이 하위 클래스 열거:

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

또 아주 실행은, 파이톤에서 파이톤에 있는 다도시의 enum다를 한 것이다.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())




# Example for dictionary param:
values = {"Salad": 20, "Carrot": 99, "Tomato": "No i'm not"} 
Vegetables= enum(**values)

# >>> print(Vegetables.Tomato)        'No i'm not'


# Example for keyworded params: 
Fruits = enum(Apple="Steve Jobs", Peach=1, Banana=2)

# >>> print(Fruits.Apple)             'Steve Jobs'

처럼 하위 위의 하한 클라스트레스.set, 이것은 다음을 허용한다.

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

그러나 그것은 다른 키와 가치를 가질 수 있기 때문에 더 많은 유연성을 가지고 있다.이것은 허용된다.

MyEnum.FOO < MyEnum.BAR

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

음... 열거형에서 가장 가까운 것은 다음과 같이 정의되는 사전일 것 같다.

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

또는

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

그런 다음 다음과 같은 상수에 기호 이름을 사용할 수 있다.

mymonth = months['January']

튜플의 목록이나 튜플의 튜플과 같은 다른 선택사항도 있지만, 그 값에 접근할 수 있는 "기호적"(정수 문자열) 방법을 제공하는 것은 사전뿐이다.

편집: 알렉산드루의 대답도 좋아!

사용하는 항목:

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

그래서 이것은 당신에게 상태와 같은 정수 상수를 준다.장고 모델에서 선택할 수 있는 Published 및 2-tule.

이전 Python 2.x를 사용하여 단순성을 유지하십시오(Python 3은 아래 참조).

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

Python 3 사용 시 단순성 유지:

from enum import Enum
class MyEnum(Enum):
    UP = 1
    DOWN = 2
    LEFT = 3
    RIGHT = 4

다음:

MyEnum.DOWN

https://docs.python.org/3/library/enum.html을 참조하십시오.

Python의 표준은 PEP 435이므로 Enum 클래스는 Python 3.4+:

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

davidg는 받아쓰기를 권장한다.한 걸음 더 나아가서 다음과 같은 세트를 사용하고 싶다.

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

이제 다음과 같은 집합의 값 중 하나와 값이 일치하는지 테스트할 수 있다.

if m in months:

하지만 dF처럼, 나는 보통 에넘 대신에 문자열 상수를 사용한다.

내가 본 것 중 최고야: "Pirst Class Enums in Python"

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

그것은 당신에게 수업을 제공하고, 그 수업은 모든 에누리를 포함한다.열거형들은 서로 비교할 수 있지만, 특별한 값을 가지고 있지 않다. 당신은 그것들을 정수 값으로 사용할 수 없다.(나는 처음에는 정수 값인 C enums에 익숙하기 때문에 이것을 저항했다.그러나 정수로 사용할 수 없다면 실수로 정수로 사용할 수 없기 때문에 전반적으로 나는 승리라고 생각한다.)각 열거형은 고유한 값이다.열거형을 인쇄할 수도 있고, 반복할 수도 있고, 열거형 값이 열거형 안에 있는지 테스트할 수도 있다.꽤 완전하고 매끈매끈하다.

편집(cfi):위의 링크는 Python 3과 호환되지 않는다.여기 enum.py에서 파이썬 3으로 가는 내 항구가 있다.

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)

이진 파일 형식을 해독하기 위해 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()모두 열거형의 이름, 정수 값, 그리고 열거로 되돌아가는 파이썬 식과 같은 가능한 가장 유용한 출력을 생산한다.
  • 생성자가 반환하는 열거된 값은 사전 정의된 값으로 엄격히 제한되며, 우발적인 열거 값은 제외된다.
  • 열거된 값은 단골격이다. 단골격과 엄격히 비교할 수 있다.is

기존 Python 2.x용

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

아이쿠스!

Alec Thomas의 솔루션(http://stackoverflow.com/a/1695250):

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

우아하고 깔끔해 보이지만, 정해진 속성으로 클래스를 만드는 기능일 뿐이다.

이 기능을 약간 수정하면 우리는 이 기능이 좀 더 '에누미'하게 할 수 있다.

참고: 나는 피그트의 새로운 스타일인 '이넘'의 행동을 재현하려고 노력하여 다음과 같은 예를 만들었다(Gtk와 같다).MessageType.경고)

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

이렇게 하면 지정된 형식에 기반한 열거형이 생성된다.이전 함수처럼 속성 액세스 권한을 부여할 뿐만 아니라 유형에 대해 Enumer를 예상하는 것처럼 동작한다.베이스 클래스도 계승한다.

예를 들어 정수 열거형:

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

PyPI의 열거형 패키지는 열거형의 강력한 구현을 제공한다.앞서 나온 답변은 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'

여기 내가 가치있다고 생각하는 몇 가지 다른 특성을 가진 접근법이 있다.

  • 허용 >과 < 어휘적 순서가 아닌 열거적 순서에 따른 비교
  • 이름, 속성 또는 색인별로 항목 주소를 지정할 수 있음: x.a, x['a] 또는 x[0]
  • [:] 또는 [-1]과 같은 슬라이싱 작업 지원

그리고 가장 중요한 것은 다른 종류의 에넘들 간의 비교를 막는다!

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

이 접근방식의 차이점을 설명하기 위해 여기에 많은 문서들이 포함되어 있다.

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

계급 상수를 에넘버에 사용하자는 알렉산드루의 제안은 꽤 효과가 있다.

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

이것은 두 가지 목적을 제공한다: a) 열거형을 예쁘게 인쇄할 수 있는 간단한 방법을 제공하고 b) 사전은 회원 자격을 테스트할 수 있도록 상수를 논리적으로 그룹화한다.

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())

원래의 열거형 제안인 PEP 354는 몇 년 전에 거절당했지만, 계속 올라오고 있다.어떤 종류의 열거형은 3.2에 추가될 예정이었지만, 3.3으로 밀렸다가 잊혀졌다.그리고 이제 PEP 435가 Python 3.4에 포함되어 있다.PEP 435의 기준 구현은 입니다.

2013년 4월 현재, 사람들이 그 "무엇"이 무엇이 되어야 하는지에 대해 동의할 수 있는 한, 3.4의 표준도서관에 무엇인가 추가되어야 한다는 일반적인 공감대가 있는 것 같다.그게 어려운 부분이야.여기에서 시작하는 스레드와 2013년 초순에 6개의 스레드를 보십시오.

한편 이 문제가 발생할 때마다 PyPI, ActiveState 등에 수많은 새로운 설계와 구현이 나타나므로 FLUFL 디자인이 마음에 들지 않으면 PyPI 검색을 시도해 보십시오.

다음을 사용하십시오.

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은 아니지만, 그것은 그 일을 한다.

이거 답안지 목록에서 못 봤어, 여기 내가 잽싸게 만든 거야.'in' 키워드 및 len() 메서드를 사용할 수 있다.

class EnumTypeError(TypeError):
    pass

class Enum(object):
    """
    Minics enum type from different languages
    Usage:
    Letters = Enum(list('abc'))
    a = Letters.a
    print(a in Letters) # True
    print(54 in Letters) # False
    """
    def __init__(self, enums):
        if isinstance(enums, dict):
            self.__dict__.update(enums)
        elif isinstance(enums, list) or isinstance(enums, tuple):
            self.__dict__.update(**dict((v,k) for k,v in enumerate(enums)))
        else:
            raise EnumTypeError

    def __contains__(self, key):
        return key in self.__dict__.values()

    def __len__(self):
        return len(self.__dict__.values())


if __name__ == '__main__':
    print('Using a dictionary to create Enum:')
    Letters = Enum(dict((v,k) for k,v in enumerate(list('abcde'))))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    print('\nUsing a list to create Enum:')
    Letters = Enum(list('abcde'))
    a = Letters.a
    print('\tIs a in e?', a in Letters)
    print('\tIs 54 in e?', 54 in Letters)
    print('\tLength of Letters enum:', len(Letters))

    try:
        # make sure we raise an exception if we pass an invalid arg
        Failure = Enum('This is a Failure')
        print('Failure')
    except EnumTypeError:
        print('Success!')

출력:

Using a dictionary to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5

Using a list to create Enum:
        Is a in e? True
        Is 54 in e? False
        Length of Letters enum: 5
Success!

여기 내가 여기서 찾은 멋진 파이톤 레시피가 있다: http://code.activestate.com/recipes/577024-yet-another-enum-for-python/

def enum(typename, field_names):
    "Create a new enumeration type"

    if isinstance(field_names, str):
        field_names = field_names.replace(',', ' ').split()
    d = dict((reversed(nv) for nv in enumerate(field_names)), __slots__ = ())
    return type(typename, (object,), d)()

사용 예:

STATE = enum('STATE', 'GET_QUIZ, GET_VERSE, TEACH')

자세한 내용은 레시피 페이지에서 확인할 수 있다.

참조URL: https://stackoverflow.com/questions/36932/how-can-i-represent-an-enum-in-python

반응형