Odpowiedzi:
Do języka Python 3.4 dodano wyliczenia, jak opisano w PEP 435 . Został również przeniesiony do wersji 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 i 2.4 na pypi.
Dla bardziej zaawansowanych technik Enum wypróbuj bibliotekę aenum (2.7, 3.3+, ten sam autor co enum34
. Kod nie jest w pełni kompatybilny między py2 i py3, np. Będziesz potrzebować __order__
w python 2 ).
enum34
, zrób$ pip install enum34
aenum
, zrób$ pip install aenum
Instalacja enum
(bez numerów) spowoduje zainstalowanie zupełnie innej i niezgodnej wersji.
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)
lub równoważnie:
class Animal(Enum):
ant = 1
bee = 2
cat = 3
dog = 4
We wcześniejszych wersjach jednym ze sposobów osiągnięcia wyliczeń jest:
def enum(**enums):
return type('Enum', (), enums)
który jest używany tak:
>>> Numbers = enum(ONE=1, TWO=2, THREE='three')
>>> Numbers.ONE
1
>>> Numbers.TWO
2
>>> Numbers.THREE
'three'
Możesz także z łatwością obsługiwać automatyczne wyliczanie za pomocą czegoś takiego:
def enum(*sequential, **named):
enums = dict(zip(sequential, range(len(sequential))), **named)
return type('Enum', (), enums)
i używane w ten sposób:
>>> Numbers = enum('ZERO', 'ONE', 'TWO')
>>> Numbers.ZERO
0
>>> Numbers.ONE
1
Obsługę konwersji wartości z powrotem na nazwy można dodać w ten sposób:
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)
Zastępuje to wszystko tą nazwą, ale przydaje się do renderowania twoich wyliczeń w danych wyjściowych. Zgłasza KeyError, jeśli odwrotne mapowanie nie istnieje. W pierwszym przykładzie:
>>> Numbers.reverse_mapping['three']
'THREE'
**named
) w funkcji wyliczania dla starszych wersji ma obsługiwać wartości niestandardowe:enum("blue", "red", "green", black=0)
Przed PEP 435 Python nie miał odpowiednika, ale można go zaimplementować.
Ja sam lubię to prostsze (widziałem w sieci kilka strasznie skomplikowanych przykładów), coś takiego ...
class Animal:
DOG = 1
CAT = 2
x = Animal.DOG
W Pythonie 3.4 ( PEP 435 ) możesz ustawić Enum jako klasę podstawową. To daje ci trochę dodatkowej funkcjonalności, opisanej w PEP. Na przykład członkowie enum różnią się od liczb całkowitych i składają się z a name
i 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"
Jeśli nie chcesz wpisywać wartości, użyj następującego skrótu:
class Animal(Enum):
DOG, CAT = range(2)
Enum
implementacje można konwertować na listy i są iterowalne . Kolejność członków jest kolejnością deklaracji i nie ma nic wspólnego z ich wartościami. Na przykład:
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
object()
.
Oto jedna implementacja:
class Enum(set):
def __getattr__(self, name):
if name in self:
return name
raise AttributeError
Oto jego użycie:
Animals = Enum(["DOG", "CAT", "HORSE"])
print(Animals.DOG)
__setattr__(self, name, value)
i być może __delattr__(self, name)
, że jeśli przypadkowo napiszesz Animals.DOG = CAT
, nie powiedzie się to po cichu.
Animals.DOG
; ponadto wartości constats są ciągami, więc porównania z tymi stałymi są wolniejsze, niż gdyby, powiedzmy, liczby całkowite były dozwolone jako wartości.
setattr()
funkcji wewnątrz __init__()
metody zamiast __getattr__()
metody overidding . Zakładam, że to powinno działać w ten sam sposób: klasa Enum (obiekt): def __init __ (self, enum_string_list): if type (enum_string_list) == list: dla enum_string w enum_string_list: setattr (self, enum_string, enum_string) else: podbicie AttributeError
try-except
bloku?
Jeśli potrzebujesz wartości liczbowych, oto najszybszy sposób:
dog, cat, rabbit = range(3)
W Pythonie 3.x możesz również dodać symbol zastępczy oznaczony gwiazdką na końcu, który pochłonie wszystkie pozostałe wartości zakresu, na wypadek gdybyś nie miał nic przeciwko marnowaniu pamięci i nie możesz liczyć:
dog, cat, rabbit, horse, *_ = range(100)
Najlepsze rozwiązanie dla Ciebie zależy od tego, czego oczekujesz od fałszywki enum
.
Proste wyliczanie:
Jeśli potrzebujesz enum
tylko listy nazw identyfikujących różne przedmioty , rozwiązanie autorstwa Mark Harrison (powyżej) jest świetne:
Pen, Pencil, Eraser = range(0, 3)
Użycie a range
pozwala również ustawić dowolną wartość początkową :
Pen, Pencil, Eraser = range(9, 12)
Oprócz powyższego, jeśli wymagasz również, aby przedmioty należały do jakiegoś kontenera , umieść je w klasie:
class Stationery:
Pen, Pencil, Eraser = range(0, 3)
Aby użyć elementu wyliczanego, musisz teraz użyć nazwy kontenera i nazwy elementu:
stype = Stationery.Pen
Złożone wyliczenie:
W przypadku długich list enum lub bardziej skomplikowanych zastosowań enum te rozwiązania nie będą wystarczające. Możesz zajrzeć do przepisu Will Ware'a na symulowanie wyliczeń w Pythonie opublikowanego w Python Cookbook . Wersja online jest dostępna tutaj .
Więcej informacji:
PEP 354: Wyliczenia w Pythonie zawierają interesujące szczegóły propozycji wyliczenia w Pythonie i dlaczego zostały odrzucone.
range
możesz pominąć pierwszy argument, jeśli ma wartość 0
my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
. Następnie my_enum
może być użyty do wyszukiwania, np. my_enum['Item0']
Może być indeksem w sekwencji. Możesz zawinąć wynik str.split
w funkcję, która zgłasza wyjątek, jeśli istnieją jakieś duplikaty.
Flag1, Flag2, Flag3 = [2**i for i in range(3)]
Bezpieczny wzorzec wyliczenia zastosowany w Javie przed JDK 5 ma wiele zalet. Podobnie jak w odpowiedzi Alexandru, tworzysz klasę, a pola na poziomie klasy są wartościami wyliczania; jednak wartości wyliczeniowe są instancjami klasy, a nie małymi liczbami całkowitymi. Ma to tę zaletę, że twoje wartości wyliczeniowe nie przypadkowo porównują wartości równe małym liczbom całkowitym, możesz kontrolować sposób ich drukowania, dodawać dowolne metody, jeśli jest to użyteczne, i dokonywać asercji za pomocą 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
Niedawny wątek na temat python-dev wykazał, że na wolności istnieje kilka bibliotek enum, w tym:
Klasa Enum może być jednowarstwowa.
class Enum(tuple): __getattr__ = tuple.index
Jak z niego korzystać (wyszukiwanie do przodu i do tyłu, klucze, wartości, pozycje itp.)
>>> 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)]
in
słowa kluczowego do wyszukania członków, którzy są porządni. Przykładowe użycie:'Claimed' in Enum(['Unclaimed', 'Claimed'])
Zgadzam się. Nie wymuszajmy bezpieczeństwa typu w Pythonie, ale chciałbym uchronić się przed głupimi błędami. Co myślimy o tym?
class Animal(object):
values = ['Horse','Dog','Cat']
class __metaclass__(type):
def __getattr__(self, name):
return self.values.index(name)
Chroni mnie przed kolizją wartości przy definiowaniu moich wyliczeń.
>>> Animal.Cat
2
Jest jeszcze jedna przydatna zaleta: naprawdę szybkie wyszukiwanie wsteczne:
def name_of(self, i):
return self.values[i]
Animal = Enum('horse', 'dog', 'cat')
. Łapię także ValueError w getattr w przypadku braku elementu w self.values - wydaje się, że lepiej jest podnieść AttributeError za pomocą dostarczonego ciągu nazwy. Nie mogłem zmusić metaklasy do pracy w Pythonie 2.7 w oparciu o moją ograniczoną wiedzę w tym zakresie, ale moja niestandardowa klasa Enum działa dobrze z metodami prostych instancji.
Python nie ma wbudowanego odpowiednika enum
, a inne odpowiedzi mają pomysły na wdrożenie własnego (możesz być również zainteresowany ponadprzeciętną wersją książki kucharskiej Python).
Jednak w sytuacjach, w których enum
w C byłoby wywoływane, zwykle używam prostych ciągów znaków : ze względu na sposób implementacji obiektów / atrybutów (C) Python jest zoptymalizowany do pracy bardzo szybko z krótkimi ciągami, więc nie to naprawdę przyniesie korzyści w postaci używania liczb całkowitych. Aby uchronić się przed literówkami / nieprawidłowymi wartościami, możesz wstawiać czeki w wybranych miejscach.
ANIMALS = ['cat', 'dog', 'python']
def take_for_a_walk(animal):
assert animal in ANIMALS
...
(Wadą w porównaniu do korzystania z klasy jest utrata korzyści autouzupełniania)
W dniu 2013-05-10 Guido zgodził się zaakceptować PEP 435 do standardowej biblioteki Python 3.4. Oznacza to, że Python wreszcie ma wbudowaną obsługę wyliczeń!
Dostępny jest backport dla Python 3.3, 3.2, 3.1, 2.7, 2.6, 2.5 i 2.4. Jest na Pypi jako enum34 .
Deklaracja:
>>> from enum import Enum
>>> class Color(Enum):
... red = 1
... green = 2
... blue = 3
Reprezentacja:
>>> print(Color.red)
Color.red
>>> print(repr(Color.red))
<Color.red: 1>
Iteracja:
>>> for color in Color:
... print(color)
...
Color.red
Color.green
Color.blue
Dostęp programowy:
>>> Color(1)
Color.red
>>> Color['blue']
Color.blue
Aby uzyskać więcej informacji, zapoznaj się z wnioskiem . Oficjalna dokumentacja prawdopodobnie nastąpi wkrótce.
Wolę definiować wyliczenia w Pythonie tak:
class Animal:
class Dog: pass
class Cat: pass
x = Animal.Dog
Jest to bardziej odporne na błędy niż używanie liczb całkowitych, ponieważ nie musisz się martwić o to, że liczby całkowite są unikalne (np. Jeśli powiesz, że Dog = 1 i Cat = 1, to byś wkręcił).
Jest bardziej odporny na błędy niż używanie ciągów, ponieważ nie musisz się martwić o literówki (np. X == „catt” zawodzi cicho, ale x == Animal.Catt to wyjątek czasu wykonywania).
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()
Użyj tego w ten sposób:
Animal = enum('DOG', 'CAT')
Animal.DOG # returns 0
Animal.CAT # returns 1
Animal.DOG = 2 # raises NotImplementedError
jeśli chcesz tylko unikatowych symboli i nie dbasz o wartości, zamień ten wiersz:
__metaclass__ = M_add_class_attribs(enumerate(names))
z tym:
__metaclass__ = M_add_class_attribs((object(), name) for name in names)
enum(names)
na enum(*names)
- wtedy możesz zostawić dodatkowy nawias podczas dzwonienia.
Hmmm ... Przypuszczam, że najbliższym wyliczeniem byłby słownik zdefiniowany w następujący sposób:
months = {
'January': 1,
'February': 2,
...
}
lub
months = dict(
January=1,
February=2,
...
)
Następnie możesz użyć nazwy symbolicznej dla stałych w następujący sposób:
mymonth = months['January']
Istnieją inne opcje, takie jak lista krotek lub krotka krotek, ale słownik jest jedyną, która zapewnia „symboliczny” (stały ciąg) sposób dostępu do wartości.
Edycja: Podoba mi się również odpowiedź Alexandru!
Kolejna, bardzo prosta implementacja wyliczenia w Pythonie, przy użyciu namedtuple
:
from collections import namedtuple
def enum(*keys):
return namedtuple('Enum', keys)(*keys)
MyEnum = enum('FOO', 'BAR', 'BAZ')
lub alternatywnie
# 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())
Podobnie jak w przypadku powyższej podklasy metoda set
ta umożliwia:
'FOO' in MyEnum
other = MyEnum.FOO
assert other == MyEnum.FOO
Ale ma większą elastyczność, ponieważ może mieć różne klucze i wartości. To pozwala
MyEnum.FOO < MyEnum.BAR
działać zgodnie z oczekiwaniami, jeśli używasz wersji, która wypełnia kolejne wartości liczbowe.
Od Python 3.4 będzie istniało oficjalne wsparcie dla wyliczeń. Dokumentację i przykłady można znaleźć tutaj na stronie dokumentacji Python 3.4 .
Wyliczenia są tworzone przy użyciu składni klas, co czyni je łatwymi do odczytu i zapisu. Alternatywną metodę tworzenia opisano w Functional API. Aby zdefiniować wyliczenie, należy podklasować Wylicz w następujący sposób:
from enum import Enum
class Color(Enum):
red = 1
green = 2
blue = 3
Czego używam:
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))
Jak używać:
>>> 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'))
To daje stałe całkowite, takie jak state.PUBLISHED i dwie krotki do użycia jako opcje w modelach Django.
davidg zaleca używanie dykt. Idę o krok dalej i używam zestawów:
months = set('January', 'February', ..., 'December')
Teraz możesz przetestować, czy wartość pasuje do jednej z wartości w zestawie, jak poniżej:
if m in months:
podobnie jak dF, zwykle używam tylko ciągów znaków zamiast wyliczeń.
To jest najlepsze, jakie widziałem: „Pierwszorzędne wyliczenia w Pythonie”
http://code.activestate.com/recipes/413486/
Daje ci klasę, a klasa zawiera wszystkie wyliczenia. Wyliczenia można porównywać ze sobą, ale nie mają one żadnej szczególnej wartości; nie można ich użyć jako wartości całkowitej. (Na początku opierałem się temu, ponieważ jestem przyzwyczajony do wyliczeń C, które są wartościami całkowitymi. Ale jeśli nie możesz użyć go jako liczby całkowitej, nie możesz użyć go jako liczby całkowitej przez pomyłkę, więc ogólnie myślę, że to wygrana .) Każde wyliczenie ma unikalną wartość. Możesz drukować wyliczenia, możesz iterować nad nimi, możesz sprawdzić, czy wartość wyliczenia jest „w” wyliczeniu. Jest całkiem kompletny i elegancki.
Edycja (cfi): Powyższy link nie jest kompatybilny z Python 3. Oto mój port enum.py dla Python 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)
.__int__()
metoda powinna zgłosić wyjątek dla wyliczenia; ale powinien istnieć sposób na uzyskanie wartości. I powinno być możliwe ustawienie określonych wartości całkowitych w czasie definicji klasy, abyś mógł użyć wyliczenia dla rzeczy takich jak stałe w stat
module.
Nie komplikuj:
class Enum(object):
def __init__(self, tupleList):
self.tupleList = tupleList
def __getattr__(self, name):
return self.tupleList.index(name)
Następnie:
DIRECTION = Enum(('UP', 'DOWN', 'LEFT', 'RIGHT'))
DIRECTION.DOWN
1
Miałem okazję potrzebować klasy Enum do dekodowania formatu pliku binarnego. Funkcje, o które mi repr
chodziło, to zwięzła definicja wyliczenia, możliwość swobodnego tworzenia wystąpień wyliczenia przez wartość całkowitą lub ciąg znaków oraz przydatna esentacja. Oto, z czym skończyłem:
>>> 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
...
Kapryśny przykład użycia:
>>> 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
Kluczowe cechy:
str()
, int()
i repr()
wszystkie dają najbardziej użyteczne wyjście, odpowiednio nazwę enumartion, jego wartość całkowitą i wyrażenie Python, które zwraca wartość do wyliczenia.is
__instancecheck__
metodę. Klasy nie są zbiorami instancji, więc 1 in Fruit
jest absurdalne. Jednak wersja połączona obsługuje, isinstance(1, Fruit)
co byłoby bardziej poprawne pod względem pojęcia klas i instancji.
Nowym standardem w Pythonie jest PEP 435 , więc klasa Enum będzie dostępna w przyszłych wersjach Pythona:
>>> from enum import Enum
Jednak, aby rozpocząć korzystanie z niego teraz, możesz zainstalować oryginalną bibliotekę, która motywowała PEP:
$ pip install flufl.enum
Następnie możesz użyć go zgodnie z jego przewodnikiem online :
>>> 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
def enum(*sequential, **named):
enums = dict(zip(sequential, [object() for _ in range(len(sequential))]), **named)
return type('Enum', (), enums)
Jeśli go nazwiesz, to jest twój problem, ale jeśli nie tworzysz obiektów zamiast wartości, możesz to zrobić:
>>> DOG = enum('BARK', 'WALK', 'SIT')
>>> CAT = enum('MEOW', 'WALK', 'SIT')
>>> DOG.WALK == CAT.WALK
False
Korzystając z innych implementacji wymienionych tutaj (również w przypadku nazwanych instancji w moim przykładzie), musisz mieć pewność, że nigdy nie próbujesz porównywać obiektów z różnych wyliczeń. Oto możliwe pułapki:
>>> 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
Yikes!
Naprawdę podoba mi się rozwiązanie Aleca Thomasa (http://stackoverflow.com/a/1695250):
def enum(**enums):
'''simple constant "enums"'''
return type('Enum', (object,), enums)
Jest elegancki i czysty, ale to tylko funkcja, która tworzy klasę o określonych atrybutach.
Po niewielkiej modyfikacji funkcji możemy sprawić, by działała nieco bardziej „enumy”:
UWAGA: Utworzyłem następujące przykłady, próbując odtworzyć zachowanie „enums” nowego stylu pygtk (takich jak 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
Tworzy to wyliczenie na podstawie określonego typu. Oprócz zapewnienia dostępu do atrybutów, podobnie jak poprzednia funkcja, zachowuje się tak, jak można oczekiwać od Enum w odniesieniu do typów. Dziedziczy również klasę podstawową.
Na przykład wyliczenia liczb całkowitych:
>>> 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
Inną interesującą rzeczą, którą można zrobić za pomocą tej metody, jest dostosowanie określonego zachowania poprzez przesłonięcie wbudowanych metod:
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'
Pakiet wyliczający od PyPI zapewnia niezawodną implementację wyliczania. Wcześniejsza odpowiedź wspominała PEP 354; zostało to odrzucone, ale propozycja została wdrożona http://pypi.python.org/pypi/enum .
Użycie jest łatwe i eleganckie:
>>> 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'
Sugestia Alexandru dotycząca użycia stałych klasowych dla wyliczeń działa całkiem dobrze.
Chciałbym również dodać słownik dla każdego zestawu stałych, aby wyszukać reprezentację ciągu czytelną dla człowieka.
Służy to dwóm celom: a) zapewnia prosty sposób wydrukowania enum ib) słownik logicznie grupuje stałe, aby można było przetestować członkostwo.
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())
Oto podejście z kilkoma różnymi cechami, które uważam za wartościowe:
i, co najważniejsze, zapobiega porównywaniu różnych rodzajów wyliczeń !
Oparte ściśle na http://code.activestate.com/recipes/413486-first-class-enums-in-python .
Wiele dokumentów zawartych tutaj w celu zilustrowania różnic w tym podejściu.
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
Oto wariant rozwiązania Aleca Thomasa :
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
To rozwiązanie jest prostym sposobem uzyskania klasy dla wyliczenia zdefiniowanego jako lista (koniec z denerwującymi przypisaniami liczb całkowitych):
enumeration.py:
import new
def create(class_name, names):
return new.classobj(
class_name, (object,), dict((y, x) for x, y in enumerate(names))
)
przyklad.py:
import enumeration
Colors = enumeration.create('Colors', (
'red',
'orange',
'yellow',
'green',
'blue',
'violet',
))
type(class_name, (object,), dict(...))
zamiast tego?
Podczas gdy pierwotna propozycja enum, PEP 354 , została odrzucona wiele lat temu, wciąż powraca. Jakiś rodzaj wyliczenia miał być dodany do 3.2, ale został przesunięty z powrotem do 3.3, a następnie zapomniany. A teraz jest PEP 435 przeznaczony do włączenia w Pythonie 3.4. Referencyjnym wdrożeniem PEP 435 jest flufl.enum
.
W kwietniu 2013 r. Wydaje się, że istnieje ogólna zgoda co do tego, że należy coś dodać do standardowej biblioteki w 3.4 - o ile ludzie mogą uzgodnić, co to powinno być. To trudna część. Zobacz wątki zaczynające się tu i tutaj oraz pół tuzina innych wątków na początku 2013 r.
Tymczasem za każdym razem, gdy to pojawia się, na PyPI, ActiveState itp. Pojawia się mnóstwo nowych projektów i implementacji, więc jeśli nie podoba ci się projekt FLUFL, spróbuj wyszukać PyPI .
Użyj następujących.
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']
Użyłem tego przy wyborze modeli Django i wygląda to bardzo pytonicznie. To naprawdę nie jest Enum, ale działa.