Jak przekonwertować int na Enum w Pythonie?

99

Korzystanie z nowej funkcji Enum (przez backport enum34 ) w Pythonie 2.7.6.

Biorąc pod uwagę następującą definicję, w jaki sposób mogę przekonwertować wartość typu int na odpowiednią wartość Enum?

from enum import Enum

class Fruit(Enum):
    Apple = 4
    Orange = 5
    Pear = 6

Wiem, że mogę ręcznie stworzyć serię instrukcji if, aby wykonać konwersję, ale czy istnieje łatwy sposób konwersji w języku Python? Zasadniczo chciałbym funkcję ConvertIntToFruit (int), która zwraca wartość wyliczenia.

Mój przypadek użycia jest taki, że mam plik rekordów csv, w którym wczytuję każdy rekord do obiektu. Jedno z pól pliku jest polem całkowitoliczbowym, które reprezentuje wyliczenie. Ponieważ wypełniam obiekt, chciałbym przekonwertować to pole liczby całkowitej z pliku na odpowiednią wartość Enum w obiekcie.

Użytkownik
źródło

Odpowiedzi:

168

Wzywasz Enumklasę:

Fruit(5)

zamienić się 5w Fruit.Orange:

>>> from enum import Enum
>>> class Fruit(Enum):
...     Apple = 4
...     Orange = 5
...     Pear = 6
... 
>>> Fruit(5)
<Fruit.Orange: 5>

Z sekcji Dostęp programowy do członków wyliczenia i ich atrybutów w dokumentacji:

Czasami przydatne jest programowe uzyskiwanie dostępu do elementów członkowskich w wyliczeniach (tj. Sytuacje, w których Color.rednie można tego zrobić, ponieważ dokładny kolor nie jest znany w czasie pisania programu). Enumumożliwia taki dostęp:

>>> Color(1)
<Color.red: 1>
>>> Color(3)
<Color.blue: 3>

W powiązanej uwadze: aby zmapować wartość ciągu zawierającą nazwę elementu członkowskiego wyliczenia, użyj subskrypcji:

>>> s = 'Apple'
>>> Fruit[s]
<Fruit.Apple: 4>
Martijn Pieters
źródło
Dzięki temu otrzymałem to, gdy ponownie czytałem dokumentację. Nie było to dla mnie jasne podczas pierwszego przejrzenia. Po prostu spróbowałem użyć ciągu znaków i miło było zobaczyć, że działa, gdy wartość wyliczenia jest również ciągiem i zakładam dowolną wartość obiektu.
Użytkownik
Bardzo dobrze, więc konwersja jest wartością. Czy istnieje odpowiednik dla według nazwy inny niż przez listowanie, aby przefiltrować listę wyliczeń i dopasować atrybuty nazwy?
jxramos
4
@jxramos podążaj za linkiem do dokumentacji w mojej odpowiedzi, jest tam wyraźnie omówiony. Użyj dostęp artykuł: Fruit['Orange'].
Martijn Pieters
Bingo, bardzo niesamowite! Szukałem aktualizacja convert, coercei cast, ale nie nic trafić. Wygląda na to, że była magia access enum members by name, use item access. Perfecto, wielkie dzięki, zamierzam wyczyścić mój kod za pomocą tej składni.
jxramos
Jeszcze fajniejsze jest to, że mogę użyć rzeczy, takich jak "foobar" in Fruit.__members__test członkostwa, a nawet zdjąć, Fruit.get( 'foobar', Fruit.Orange )aby uzyskać domyślny smak Enum. Mój kod wygląda na znacznie bardziej uproszczony, usuwając wszystkie te rusztowania wyszukiwania akcesoriów, które opisałem wcześniej.
jxramos
1

Myślę, że w prostych słowach należy przekonwertować intwartość na Enumwywołanie EnumType(int_value), a następnie uzyskać dostęp namedo Enumobiektu:

my_fruit_from_int = Fruit(5) #convert to int
fruit_name = my_fruit_from_int.name #get the name
print(fruit_name) #Orange will be printed here

Lub jako funkcja:

def convert_int_to_fruit(int_value):
    try:
        my_fruit_from_int = Fruit(int_value)
        return my_fruit_from_int.name
    except:
        return None
Ali Ezzat Odeh
źródło
-1

Chciałem czegoś podobnego, aby móc uzyskać dostęp do dowolnej części pary wartości z jednego odniesienia. Wersja waniliowa:

#!/usr/bin/env python3


from enum import IntEnum


class EnumDemo(IntEnum):
    ENUM_ZERO       = 0
    ENUM_ONE        = 1
    ENUM_TWO        = 2
    ENUM_THREE      = 3
    ENUM_INVALID    = 4


#endclass.


print('Passes')
print('1) %d'%(EnumDemo['ENUM_TWO']))
print('2) %s'%(EnumDemo['ENUM_TWO']))
print('3) %s'%(EnumDemo.ENUM_TWO.name))
print('4) %d'%(EnumDemo.ENUM_TWO))
print()


print('Fails')
print('1) %d'%(EnumDemo.ENUM_TWOa))

Niepowodzenie zgłasza wyjątek, zgodnie z oczekiwaniami.

Bardziej solidna wersja:

#!/usr/bin/env python3


class EnumDemo():


    enumeration =   (
                        'ENUM_ZERO',    # 0.
                        'ENUM_ONE',     # 1.
                        'ENUM_TWO',     # 2.
                        'ENUM_THREE',   # 3.
                        'ENUM_INVALID'  # 4.
                    )


    def name(self, val):
        try:

            name = self.enumeration[val]
        except IndexError:

            # Always return last tuple.
            name = self.enumeration[len(self.enumeration) - 1]

        return name


    def number(self, val):
        try:

            index = self.enumeration.index(val)
        except (TypeError, ValueError):

            # Always return last tuple.
            index = (len(self.enumeration) - 1)

        return index


#endclass.


print('Passes')
print('1) %d'%(EnumDemo().number('ENUM_TWO')))
print('2) %s'%(EnumDemo().number('ENUM_TWO')))
print('3) %s'%(EnumDemo().name(1)))
print('4) %s'%(EnumDemo().enumeration[1]))
print()
print('Fails')
print('1) %d'%(EnumDemo().number('ENUM_THREEa')))
print('2) %s'%(EnumDemo().number('ENUM_THREEa')))
print('3) %s'%(EnumDemo().name(11)))
print('4) %s'%(EnumDemo().enumeration[-1]))

Gdy nie jest używany poprawnie, pozwala to uniknąć tworzenia wyjątku i zamiast tego przekazuje z powrotem wskazanie błędu. Bardziej Pythonowym sposobem na zrobienie tego byłoby przekazanie z powrotem „Brak”, ale moja konkretna aplikacja używa tego tekstu bezpośrednio.

user3355323
źródło
Dlaczego po prostu nie zdefiniować metod __int__i __str__w instancji Enum?
Tim