Wie teste ich, ob der int-Wert in Python Enum vorhanden ist, ohne try / catch zu verwenden?

75

Gibt es eine Möglichkeit, mit der Python Enum-Klasse zu testen, ob eine Enum einen bestimmten int-Wert enthält, ohne try / catch zu verwenden?

Mit folgender Klasse:

from enum import Enum

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

Wie kann ich den Wert 6 (Rückgabe von true) oder den Wert 7 (Rückgabe von false) testen?

Nathan Kovner
quelle

Antworten:

117

auf Werte prüfen

Variante 1

Beachten Sie, dass ein EnumMitglied aufgerufen wird _value2member_map_(das nicht dokumentiert ist und in zukünftigen Python-Versionen geändert / entfernt werden kann):

print(Fruit._value2member_map_)
# {4: <Fruit.Apple: 4>, 5: <Fruit.Orange: 5>, 6: <Fruit.Pear: 6>}

Sie können anhand Enumdieser Karte testen, ob sich ein Wert in Ihrem befindet :

5 in Fruit._value2member_map_  # True
7 in Fruit._value2member_map_  # False

Variante 2

Wenn Sie sich nicht auf diese Funktion verlassen möchten, ist dies eine Alternative:

values = [item.value for item in Fruit]  # [4, 5, 6]

oder (wahrscheinlich besser): benutze a set; Der inBediener wird effizienter:

values = set(item.value for item in Fruit)  # {4, 5, 6}

dann testen mit

5 in values  # True
7 in values  # False

füge has_valuedeiner Klasse hinzu

Sie können dies dann als Methode zu Ihrer Klasse hinzufügen:

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

    @classmethod
    def has_value(cls, value):
        return value in cls._value2member_map_ 

print(Fruit.has_value(5))  # True
print(Fruit.has_value(7))  # False

Test auf Schlüssel

Wenn Sie nach den Namen (und nicht nach den Werten) testen möchten, würde ich Folgendes verwenden _member_names_:

'Apple' in Fruit._member_names_  # True
'Mango' in Fruit._member_names_  # False
Hiro-Protagonist
quelle
4
Ich persönlich bevorzuge die zweite Lösung. Vielen Dank.
Aliakbar Abbasi
1
Entweder ist die Beratung nicht besonders speicher- oder zeiteffizient, obwohl sie in diesen Begriffen auch nicht sehr schwer ist. In meinem Fall arbeite ich mit der HTTPStatusAufzählung aus der Standardbibliothek, die ab sofort 57 Einträge enthält. Ich würde niemals eine ganze Liste von Werten aus einer Aufzählung verarbeiten, und das Zwischenspeichern dieser Werte möchte ich vermeiden.
Acsor
@none Wenn Sie sich Sorgen um die Geschwindigkeit machen, können Sie eine setfür die Suche erstellen . das würde etwas zusätzlichen Speicher
verbrauchen,
Warum die Klammern um den Rückgabewert?
Konstantin
Musste sich return any(value == item.value[0] for item in cls) bei Python3 die Enum-Struktur ändern?
Lony
19

Sie könnten verwenden Enum.__members__- ein geordnetes Wörterbuch, das den Mitgliedern Namen zuordnet :

In [12]: 'Apple' in Fruit.__members__
Out[12]: True

In [13]: 'Grape' in Fruit.__members__
Out[13]: False
Reda Maachi
quelle
5
Die Frage lautet, auf einen int-Wert zu testen, nicht auf eine Zeichenfolge.
Nathan Kovner
6
Gibt es in Enum kein Mitglied, das die Werte enthält? Ist das nicht seltsam?
Konstantin
4
Dies geht in die entgegengesetzte Richtung wie die gestellte Frage.
user2357112 unterstützt Monica
5

Aufbauend auf dem, was Reda Maachi begonnen hat:

6 in Fruit.__members__.values() 

gibt True zurück

7 in Fruit.__members__.values()  

gibt False zurück

manu3d
quelle
2
Das sah nach einem sehr sauberen Weg aus. Als ich das getestet habe (zumindest in Py3.7), funktioniert es leider nicht so. Fruit .__ Mitglieder __. Values ​​() ergibt odict_values ​​([<Fruit.Apple: 4>, <Fruit.Orange: 5>, <Fruit.Pear: 6>]), und beide obigen Tests für 6 und 7 geben False for zurück mich. Aber dies gibt wahr zurück:>>> Fruit.Orange in Fruit.__members__.values() True
nrshapiro
4
Ich möchte darauf hinweisen, dass dies nur so funktioniert, wie es für IntEnum geschrieben wurde.
Yandros
3

Überprüfen Sie einfach, ob es in ist Enum. _value2member_map_

In[15]: Fruit._value2member_map_
Out[15]: {4: <Fruit.Apple: 4>, 5: <Fruit.Orange: 5>, 6: <Fruit.Pear: 6>}

In[16]: 6 in Fruit._value2member_map_
Out[16]: True

In[17]: 7 in Fruit._value2member_map_
Out[17]: False
JianWei
quelle
4
_value2member_map_ist eine undokumentierte Funktion, daher ist es am besten, sie nicht zu verwenden.
Palasaty
3

Sie können ein __members__spezielles Attribut verwenden , um Mitglieder zu durchlaufen:

from enum import Enum

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

    @staticmethod
    def has_value(item):
        return item in [v.value for v in Connection.__members__.values()]
Alex Jolig
quelle
3

Es gibt eine Möglichkeit, dass alle Aufzählungen überprüfen können, ob ein Element vorhanden ist:

import enum 

class MyEnumMeta(enum.EnumMeta): 
    def __contains__(cls, item): 
        return item in [v.value for v in cls.__members__.values()] 

class MyEnum(enum.Enum, metaclass=MyEnumMeta): 
   FOO = "foo" 
   BAR = "bar"

Jetzt können Sie eine einfache Überprüfung durchführen:

>>> "foo" in MyEnum
True

Es kann sogar einfacher gemacht werden, wenn alle Werte der Aufzählung immer vom gleichen Typ sind - zum Beispiel Zeichenfolgen:

import enum 
 
class MyEnumMeta(enum.EnumMeta):  
    def __contains__(cls, item): 
        return item in cls.__members__.values()

class MyEnum(str, enum.Enum, metaclass=MyEnumMeta): 
    FOO = "foo" 
    BAR = "bar"
Berislav Lopac
quelle
2

Es gibt noch eine andere Einzeiler-Lösung, die noch niemand erwähnt hat:

is_value_in_fruit = any(f.value == value_to_check for f in Fruit)

Wenn Sie IntEnumanstelle von Enum( class Fruit(IntEnum)) verwenden, können Sie dies auch einfach tun

is_value_in_fruit = any(f == value_to_check for f in Fruit) 
Joshua Ryan
quelle
1

Wenn die Aufzählung viele Mitglieder hat, kann dieser Ansatz schneller sein, da keine neue Liste erstellt wird und die Aufzählung nicht mehr durchlaufen wird, wenn der angegebene Wert gefunden wurde:

any(x.value == 5 for x in Fruit)  # True
any(x.value == 7 for x in Fruit)  # False
Finesse
quelle
0

Tu es nicht.

Wenn Sie Enum verwenden, können Sie mit auf Enum testen

     if isinstance(key, Fruit):

Aber ansonsten versuchen Sie .. ist die pythonische Methode, um auf Enum zu testen. In der Tat für jeden Bruch im Paradigma der Ententypisierung.

Die richtige und pythonische Methode zum Testen eines Int in einem IntEnum besteht darin, es auszuprobieren und einen ValueError abzufangen, wenn ein Fehler auftritt.

Viele der oben vorgeschlagenen Lösungen sind aktiv veraltet und werden von 3.8 nicht mehr zugelassen ("DeprecationWarning: Die Verwendung von Nicht-Enums bei Containment-Prüfungen führt zu TypeError in Python 3.8").

Wenn Sie wirklich nicht daran interessiert sind, Ihren Code modern zu halten, können Sie ihn einfach verwenden

if key in Fruit:
Konchog
quelle
1
Diese Lösung ist eigentlich falsch. Da OP gefragt hat, wie überprüft werden soll, ob sich ein Wert in einer Aufzählung befindet, z. B. 6 oder 7. Dies gibt immer false zurück, da keines der Mitglieder der Aufzählung tatsächlich gleich 6 oder 7 ist. Probieren Sie es aus.
Joshua Ryan
0

IntEnum + __members__

Sie können das erforderliche Verhalten verwenden IntEnumund __members__erreichen:

from enum import IntEnum

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

>>> 6 in Fruit.__members__.values()
True
>>> 7 in Fruit.__members__.values()
False

Enum + Listenverständnis + .value

Wenn Sie bleiben müssen / wollen Enum, können Sie Folgendes tun:

>>> 6 in [f.value for f in Fruit]
True
>>> 7 in [f.value for f in Fruit]
False

EAPF + ValueError

Oder Sie können leichter um Vergebung bitten als um die Erlaubnismethode :

try:
    Fruit(x)
except ValueError:
    return False
else:
    return True
Maciek
quelle
0

Eine EAFP- Version der Antwort:

try: 
    Fruit(val)
    return True
except ValueError:
    return False
ComeOnGetMe
quelle