2016-06-24 4 views
5

나는 다음과 같습니다 파이썬에서 간단한 열거 있습니다파이썬 열거 형에서 다음 값과 이전 값을 어떻게 우아하게 찾을 수 있습니까?

내가 원하는
from enum import Enum 

class MyEnum(Enum): 
    #All members have increasing non-consecutive integer values. 
    A = 0 
    B = 2 
    C = 10 
    D = 18 
    ... 

기능 pred()MyEnum 반환 선행과 (와 마찬가지로 각각 지정된 요소를, 성공 MyEnum의 멤버의 멤버를 제공 succ()the functions of the same name in Haskell). 예를 들어 succ(MyEnum.B)pred(MyEnum.D)은 모두 MyEnum.C을 반환해야합니다. 첫 번째 구성원에서 pred의 마지막 구성원을 호출 할 때 succ이 호출되면 예외가 발생할 수 있습니다.

이 작업을 수행하는 메소드가 내장되어 있지 않은 것 같습니다. 그리고 iter(MyEnum)을 호출하여 값 전체를 반복 할 수 있지만 처음부터 전체 열거 형을 거쳐야합니다. 아마 독자적으로이 작업을 수행하기 위해 부실 루프를 구현할 수는 있지만이 사이트에는 실제 파이썬 전문가가 있다는 것을 알고 있습니다. 그래서 당신에게 묻습니다. 더 나은 접근 방법이 있습니까? 분명히이 효율적이다

>>> import myenums 
>>> myenums.Sequential.B.succ() 
<Sequential.C: 4> 
>>> myenums.Sequential.B.succ().succ() 
<Sequential.D: 8> 
>>> myenums.Sequential.B.succ().succ().pred() 
<Sequential.C: 4> 

실제로 계산하는 간단한 방법이있는 경우에만 : 당신이 Enum 클래스 내부 succpred 방법을 제공 할 수

+0

"그런 식으로 열거 가능한 세트에 해당하는 수학적 데이터 유형을 제공하는 것은 아닙니다. 하스켈의'enum'의 목적은 약간 다릅니다. 어쨌든'myEnum'의 메소드로'pred'와'succ'를 구현하는 것을 막을 수는 없습니다. – Bakuriu

+0

그 질문에서 대답의 간단한 번역을 제공했습니다. –

답변

2

참고 :로

class Sequential(Enum): 
    A = 1 
    B = 2 
    C = 4 
    D = 8 
    E = 16 

    def succ(self): 
     v = self.value * 2 
     if v > 16: 
      raise ValueError('Enumeration ended') 
     return Sequential(v) 

    def pred(self): 
     v = self.value // 2 
     if v == 0: 
      raise ValueError('Enumeration ended') 
     return Sequential(v) 

사용 항목에서 다음 또는 이전 값까지의 값으로, 항상 그런 것은 아닙니다.

공간을 추가하는 대신 일반적인 효율적인 솔루션을 원할 경우 후속 기능과 선행 기능의 매핑을 만들 수 있습니다. 당신은 (Enum 놨 최대 속성 때문에) 클래스의 후 생성 속성 그래서 당신이 할 수있는 장식을 사용하여 이러한 추가해야합니다 :

>>> myenums.MyEnum.A.succ() 
<MyEnum.B: 2> 
>>> myenums.MyEnum.B.succ() 
<MyEnum.C: 8> 
>>> myenums.MyEnum.B.succ().pred() 
<MyEnum.B: 2> 
>>> myenums.MyEnum._succ_map 
{<MyEnum.A: 0>: <MyEnum.B: 2>, <MyEnum.C: 8>: <MyEnum.D: 18>, <MyEnum.B: 2>: <MyEnum.C: 8>} 

:

def add_succ_and_pred_maps(cls): 
    succ_map = {} 
    pred_map = {} 
    cur = None 
    nxt = None 
    for val in cls.__members__.values(): 
     if cur is None: 
      cur = val 
     elif nxt is None: 
      nxt = val 

     if cur is not None and nxt is not None: 
      succ_map[cur] = nxt 
      pred_map[nxt] = cur 
      cur = nxt 
      nxt = None 
    cls._succ_map = succ_map 
    cls._pred_map = pred_map 

    def succ(self): 
     return self._succ_map[self] 

    def pred(self): 
     return self._pred_map[self] 

    cls.succ = succ 
    cls.pred = pred 
    return cls 





@add_succ_and_pred_maps 
class MyEnum(Enum): 
    A = 0 
    B = 2 
    C = 8 
    D = 18 

로 사용 KeyError 대신 사용자 정의 예외를 원하지만 아이디어를 얻을 수 있습니다.


아마 메타 클래스를 사용하여 마지막 단계를 통합하는 방법이 있지만, Enum의이 메타 클래스를 사용하여 구현하고 메타 클래스를 구성하는 사소한 아니라 단순한 사실에 대한 notstraightforward입니다.

+0

귀하의 답변은 두 가지면에서 매우 도움이되었습니다. 문제가 해결되어 솔루션이 복잡해 졌기 때문에 열거 형은 내가 파이썬에서 수행하려고 시도하는 것에 대한 데이터 구조의 부실한 선택이라고 상당히 확신합니다.협조 해 주셔서 감사합니다. – ApproachingDarknessFish

1

당신의 nextprev 방법 (또는 succpred) 추가 쉽게 충분하다 :

def next(self): 
    cls = self.__class__ 
    members = list(cls) 
    index = members.index(self) + 1 
    if index >= len(members): 
     # to cycle around 
     # index = 0 
     # 
     # to error out 
     raise StopIteration('end of enumeration reached') 
    return members[index] 

def prev(self): 
    cls = self.__class__ 
    members = list(cls) 
    index = members.index(self) - 1 
    if index < 0: 
     # to cycle around 
     # index = len(members) - 1 
     # 
     # to error out 
     raise StopIteration('beginning of enumeration reached') 
    return members[index] 
파이썬에서 enum``의 목적은 "매직 넘버 대신에 사용할 수있는 데이터 형식을 제공하는 것입니다
+0

'index' 메소드는 요소가 발견되지 않았을 때'ValueError'를 발생시킵니다. 발견되지 않으면'find' 메소드를 사용하여'-1'을 얻고 싶습니다. 또한이 솔루션은 모든 구성원을 적어도 한 번 또는 두 번 이상 통과해야하므로 효율적이지 않습니다. – Bakuriu

+0

@Bakuriu : 회원이 항상 발견되기 때문에'.index()'는이 경우 사용할 수 있습니다. 평균적으로 멤버의 절반 만 통과하고 결코 두 번 통과하지 못합니다. –

+0

아니요,'list (cls)'는 ** all ** 요소를 한 번 통과 한 다음 평균으로 members.index가 구성원의 절반을 차지하지만 모든 호출마다 모든 구성원이 한 번 이상 걸립니다. 'for i, member in enumerate (cls) '루프를 수행하고 각 반복의 이전 요소를 추적하는 것이 더 효율적입니다. – Bakuriu