Jestem głównie programistą C #, ale obecnie pracuję nad projektem w Pythonie.
Jak mogę reprezentować odpowiednik Enum w Pythonie?
źródło
Jestem głównie programistą C #, ale obecnie pracuję nad projektem w Pythonie.
Jak mogę reprezentować odpowiednik Enum w Pythonie?
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 ...
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 avalue
.Jeśli nie chcesz wpisywać wartości, użyj następującego skrótu:
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:źródło
object()
.Oto jedna implementacja:
Oto jego użycie:
źródło
__setattr__(self, name, value)
i być może__delattr__(self, name)
, że jeśli przypadkowo napiszeszAnimals.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 AttributeErrortry-except
bloku?Jeśli potrzebujesz wartości liczbowych, oto najszybszy sposób:
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ć:
źródło
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:Użycie a
range
pozwala również ustawić dowolną wartość początkową :Oprócz powyższego, jeśli wymagasz również, aby przedmioty należały do jakiegoś kontenera , umieść je w klasie:
Aby użyć elementu wyliczanego, musisz teraz użyć nazwy kontenera i nazwy elementu:
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.
źródło
range
możesz pominąć pierwszy argument, jeśli ma wartość 0my_enum = dict(map(reversed, enumerate(str.split('Item0 Item1 Item2'))))
. Następniemy_enum
może być użyty do wyszukiwania, np.my_enum['Item0']
Może być indeksem w sekwencji. Możesz zawinąć wynikstr.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:
Niedawny wątek na temat python-dev wykazał, że na wolności istnieje kilka bibliotek enum, w tym:
źródło
Klasa Enum może być jednowarstwowa.
Jak z niego korzystać (wyszukiwanie do przodu i do tyłu, klucze, wartości, pozycje itp.)
źródło
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?
Chroni mnie przed kolizją wartości przy definiowaniu moich wyliczeń.
Jest jeszcze jedna przydatna zaleta: naprawdę szybkie wyszukiwanie wsteczne:
źródło
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.(Wadą w porównaniu do korzystania z klasy jest utrata korzyści autouzupełniania)
źródło
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:
Reprezentacja:
Iteracja:
Dostęp programowy:
Aby uzyskać więcej informacji, zapoznaj się z wnioskiem . Oficjalna dokumentacja prawdopodobnie nastąpi wkrótce.
źródło
Wolę definiować wyliczenia w Pythonie tak:
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).
źródło
Użyj tego w ten sposób:
jeśli chcesz tylko unikatowych symboli i nie dbasz o wartości, zamień ten wiersz:
z tym:
źródło
enum(names)
naenum(*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:
lub
Następnie możesz użyć nazwy symbolicznej dla stałych w następujący sposób:
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!
źródło
Kolejna, bardzo prosta implementacja wyliczenia w Pythonie, przy użyciu
namedtuple
:lub alternatywnie
Podobnie jak w przypadku powyższej podklasy metoda
set
ta umożliwia:Ale ma większą elastyczność, ponieważ może mieć różne klucze i wartości. To pozwala
działać zgodnie z oczekiwaniami, jeśli używasz wersji, która wypełnia kolejne wartości liczbowe.
źródło
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 .
źródło
Czego używam:
Jak używać:
To daje stałe całkowite, takie jak state.PUBLISHED i dwie krotki do użycia jako opcje w modelach Django.
źródło
davidg zaleca używanie dykt. Idę o krok dalej i używam zestawów:
Teraz możesz przetestować, czy wartość pasuje do jednej z wartości w zestawie, jak poniżej:
podobnie jak dF, zwykle używam tylko ciągów znaków zamiast wyliczeń.
źródło
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:
źródło
.__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 wstat
module.Nie komplikuj:
Następnie:
źródło
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:Kapryśny przykład użycia:
Kluczowe cechy:
str()
,int()
irepr()
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
źródło
__instancecheck__
metodę. Klasy nie są zbiorami instancji, więc1 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:
Jednak, aby rozpocząć korzystanie z niego teraz, możesz zainstalować oryginalną bibliotekę, która motywowała PEP:
Następnie możesz użyć go zgodnie z jego przewodnikiem online :
źródło
Jeśli go nazwiesz, to jest twój problem, ale jeśli nie tworzysz obiektów zamiast wartości, możesz to zrobić:
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:
Yikes!
źródło
Naprawdę podoba mi się rozwiązanie Aleca Thomasa (http://stackoverflow.com/a/1695250):
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”:
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:
Inną interesującą rzeczą, którą można zrobić za pomocą tej metody, jest dostosowanie określonego zachowania poprzez przesłonięcie wbudowanych metod:
źródło
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:
źródło
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.
źródło
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.
źródło
Oto wariant rozwiązania Aleca Thomasa :
źródło
To rozwiązanie jest prostym sposobem uzyskania klasy dla wyliczenia zdefiniowanego jako lista (koniec z denerwującymi przypisaniami liczb całkowitych):
enumeration.py:
przyklad.py:
źródło
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 .
źródło
Użyj następujących.
Użyłem tego przy wyborze modeli Django i wygląda to bardzo pytonicznie. To naprawdę nie jest Enum, ale działa.
źródło