- 작성시간 : 2013/06/05 11:23
- 퍼머링크 : mcchae.egloos.com/11027030
- 덧글수 : 7
C나 C# 등에서는 enum 형이 있습니다.
type(object) type(name, bases, dict)
다음은 C인 경우의 예 입니다.
typedef _three enum {
ZERO,
ONE,
TWO
} t_three;
라는 식으로 지정하면,
소스 내에서
t_three a;
int i = a.ONE;
과 같은 식으로 지정할 수 있습니다.
파이썬에서는 그에 해당되는 디폴트 enum 형이 없었습니다.
그래서 찾아보았더니,
해당 스택오버플로우에 그 답이 있더군요.
역시나 수많은 개발자들이 물어보았더군요.
결론은 다음과 같습니다.
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)
라는 함수를 만들면 간단히 해결됩니다.
그러면,
>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
와 같이 접근 가능하구요,
그 반대도 찾을 수 있습니다.
>>> Numbers.reverse_mapping[1]
'ONE'
와 같은 식이지요.
우와 이런 함수를 어떻게 생각하고 만들었을까 궁금해 지더군요.
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)
하나씩 뜯어 보았습니다.
>>> Numbers = enum('ZERO', 'ONE', 'TWO', FIVE=5, SIX=6)
라고 호출해 보겠습니다
enums = dict(zip(sequential, range(len(sequential))), **named)
>>> sequential
('ONE', 'TWO', 'THREE')
정상 적인 패러미터 입니다.
>>> named
{'SIX': 6, 'FIVE': 5}
값을 지정한 패러미터 입니다.
>>> zip(sequential, range(len(sequential)))
[('ONE', 0), ('TWO', 1), ('THREE', 2)]
정상 패러미터를 zip 을 이용하여 0,1,2 인덱싱을 갖는 순서쌍으로 만듦니다.
>>> dict(zip(sequential, range(len(sequential))), **named)
{'FIVE': 5, 'SIX': 6, 'THREE': 2, 'TWO': 1, 'ONE': 0}
나머지 named parameter도 같이 포함한 dict로 만듦니다.
>>> enums=dict(zip(sequential, range(len(sequential))), **named)
결국 enums 는 위와 같은 결과를 갖습니다.
reverse = dict((value, key) for key, value in enums.iteritems())
>>> dict((value, key) for key, value in enums.iteritems())
{0: 'ONE', 1: 'TWO', 2: 'THREE', 5: 'FIVE', 6: 'SIX'}
reverse는 위와 같은 key,value가 뒤집힌 dict를 갖습니다.
enums['reverse_mapping'] = reverse
>>> enums
{'reverse_mapping': {0: 'ONE', 1: 'TWO', 2: 'THREE', 5: 'FIVE', 6: 'SIX'}, 'SIX': 6, 'THREE': 2, 'TWO': 1, 'FIVE': 5, 'ONE': 0}
reverse_mapping을 포함한 dict 가 됩니다.
return type('Enum', (), enums)
이제 마지막 줄 입니다.
type() 은 python의 built-in 함수 입니다.
With one argument, return the type of an object. The return value is a type object. The isinstance() built-in function is recommended for testing the type of an object.
With three arguments, return a new type object. This is essentially a dynamic form of the class statement. Thename string is the class name and becomes the __name__ attribute; the bases tuple itemizes the base classes and becomes the __bases__ attribute; and the dict dictionary is the namespace containing definitions for class body and becomes the __dict__ attribute. For example, the following two statements create identical type objects:
New in version 2.2.
위와 같은 설명을 갖습니다.
즉 그 결과값은
x.__name__ 은 'Enum' 이고,
x.__bases__는 () 이며,
x. 안의 내용은 enums 인 type 이 되는 것이지요.
정말이지 이런식으로 dynamic 하게 언어를 만들었다는 것 자체도 훌륭하거니와
더불어 이런식의 결과를 만드는 프로그래머들도 참 대단하다는 생각이 듭니다.
참고로, 아래와 같은 직접 만드는 방법과,
Numbers=enum('ZERO', 'ONE','TWO','THREE',FIVE=5,SIX=6)
print Numbers.FIVE
print Numbers.reverse_mapping[6]
tuple과 dict 패러미터로 만드는 방법은
seq=('ZERO', 'ONE','TWO','THREE')
named={'FIVE':5, 'SIX':6}
Numbers=enum(*seq,**named)
print Numbers.FIVE
print Numbers.reverse_mapping[6]
동일한 결과를 나타냅니다.
어느분께는 도움이 되셨기를...
덧글
python 궁금한 점을 검색해보면 문창님을 뵙겠구나 하며 말이죠.
오늘 드디어 만났네요. ^^
enum function은 참 독특하네요.
고맙습니다 ^^
reverse_mapping[i] 와 같이 사용하고 싶은데 enum 타입의 개수를 알 수 있나요?
len(..)와 같이 사용하면 에러를 발생시키네요..