How do I test if int value exists in Python Enum without using try/catch?
Question:
Using the Python Enum class, is there a way to test if an Enum contains a specific int value without using try/catch?
With the following class:
from enum import Enum
class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6
How can I test for the value 6 (returning true), or the value 7 (returning false)?
Answers:
You could use Enum.__members__
– an ordered dictionary mapping names to members:
In [12]: 'Apple' in Fruit.__members__
Out[12]: True
In [13]: 'Grape' in Fruit.__members__
Out[13]: False
test for values
variant 1
note that an Enum
has a member called _value2member_map_
(which is undocumented and may be changed/removed in future python versions):
print(Fruit._value2member_map_)
# {4: <Fruit.Apple: 4>, 5: <Fruit.Orange: 5>, 6: <Fruit.Pear: 6>}
you can test if a value is in your Enum
against this map:
5 in Fruit._value2member_map_ # True
7 in Fruit._value2member_map_ # False
variant 2
if you do not want to rely on this feature this is an alternative:
values = [item.value for item in Fruit] # [4, 5, 6]
or (probably better): use a set
; the in
operator will be more efficient:
values = set(item.value for item in Fruit) # {4, 5, 6}
then test with
5 in values # True
7 in values # False
add has_value
to your class
you could then add this as a method to your class:
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
starting form python 3.9 (?) python offers IntEnum
. with these you could do this:
from enum import IntEnum
class Fruit(IntEnum):
Apple = 4
Orange = 5
Pear = 6
print(6 in iter(Fruit)) # True
note there is no need to create a list
; just iterating over iter(Fruit)
will do. again, if this is needed repeatedly it may be worth creating a set as above:
values = set(Fruit)
print(5 in values) # True
test for keys
if you want to test for the names (and not the values) i would use _member_names_
:
'Apple' in Fruit._member_names_ # True
'Mango' in Fruit._member_names_ # False
Just check whether it’s in 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
Building on what Reda Maachi started:
6 in Fruit.__members__.values()
returns True
7 in Fruit.__members__.values()
returns False
Originally, Don’t.
If you are using Enum, you can test for enum with
if isinstance(key, Fruit):
But otherwise, try.. is the pythonic way to test for enum. Indeed, for any break in the duck-typing paradigm.
The correct, and pythonic, way of testing for an int in an IntEnum is to give it a go and to catch a ValueError if there’s a failure.
Many of the solutions proposed above are actively deprecated and will be disallowed by 3.8 ( "DeprecationWarning: using non-Enums in containment checks will raise TypeError in Python 3.8" )
From Python 3.8 – Coerce it into a list.
After 3.8 or so, you can test by coercion of the enum into a list as follows (using IntEnum)
from enum import IntEnum
class Fruit(IntEnum):
Kiwi = 2
Apple = 4
Orange = 5
Pear = 6
for x in range(8):
print(f'{x}, {x in list(Fruit)}')
Which will print as follows
0, False
1, False
2, True
3, False
4, True
5, True
6, True
7, False
An EAFP version of the answer:
try:
Fruit(val)
return True
except ValueError:
return False
You could use __members__
special attribute to iterate over members:
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 Fruit.__members__.values()]
There’s another one liner solution nobody has mentioned yet:
is_value_in_fruit = any(f.value == value_to_check for f in Fruit)
Also, if you use IntEnum
instead of Enum
, (class Fruit(IntEnum)
) you can just do this
is_value_in_fruit = any(f == value_to_check for f in Fruit)
IntEnum
+ __members__
You could use IntEnum
and __members__
to achieve required behaviour:
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
+ list comprehension + .value
If you must/want stick to Enum
, you can do:
>>> 6 in [f.value for f in Fruit]
True
>>> 7 in [f.value for f in Fruit]
False
EAPF + ValueError
Or you can use easier to ask for forgiveness than permission method:
try:
Fruit(x)
except ValueError:
return False
else:
return True
There is a way to have all the enums be able to check if an item is present:
import enum
class MyEnumMeta(enum.EnumMeta):
def __contains__(cls, item):
return isinstance(item, cls) or item in [v.value for v in cls.__members__.values()]
class MyEnum(enum.Enum, metaclass=MyEnumMeta):
FOO = "foo"
BAR = "bar"
Now you can do an easy check:
>>> "foo" in MyEnum
True
It can even be made simpler if all the enum’s values will always be the same type — for example strings:
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"
Edit: As per the comments by @MestreLion, I’ve updated the first example to include the isinstance
check; without it the test fails if we’re checking an already instantiated enum value.
Edit: Yet another version, technically the most correct one:
import enum
class MyEnumMeta(enum.EnumMeta):
def __contains__(cls, item):
try:
cls(item)
except ValueError:
return False
else:
return True
class MyEnum(enum.Enum, metaclass=MyEnumMeta):
FOO = "foo"
BAR = "bar"
If the enum has many members, this approach can be faster because it doesn’t make a new list and stops walking the enum when the given value is found:
any(x.value == 5 for x in Fruit) # True
any(x.value == 7 for x in Fruit) # False
how about this?
from enum import Enum
class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6
has_apples = 4 in [n.value for n in Fruit]
This would let you also do:
has_apples = "Apple" in [n.name for n in Fruit]
I just convert an IntEnum to a list and test it normally:
from enum import IntEnum
class Foo(IntEnum):
ONE = 1
TWO = 2
THREE = 3
print(1 in list(Foo))
True
print(4 in list(Foo))
False
class MyEnumMixin:
raw_values = None # for IDE autocomplete
def __new__(cls, value):
if 'raw_values' not in cls.__dict__:
cls.raw_values = set()
cls.raw_values.add(value)
if cls.__bases__[0] is MyEnumMixin:
member = object().__new__(cls)
else:
member = super().__new__(cls, value)
member._value_ = value
return member
class MyEnum(MyEnumMixin, Enum):
FOO = 1
BAR = 'bar'
print(1 in MyEnum.raw_values)
print('bar' in MyEnum.raw_values)
class MyStrEnumMixin(MyEnumMixin, str):
pass
class MyStrEnum(MyStrEnumMixin, Enum):
FOO = 'foo'
BAR = 'bar'
print('foo' in MyStrEnum.raw_values)
print('bar' in MyStrEnum.raw_values)
Using the Python Enum class, is there a way to test if an Enum contains a specific int value without using try/catch?
With the following class:
from enum import Enum
class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6
How can I test for the value 6 (returning true), or the value 7 (returning false)?
You could use Enum.__members__
– an ordered dictionary mapping names to members:
In [12]: 'Apple' in Fruit.__members__
Out[12]: True
In [13]: 'Grape' in Fruit.__members__
Out[13]: False
test for values
variant 1
note that an Enum
has a member called _value2member_map_
(which is undocumented and may be changed/removed in future python versions):
print(Fruit._value2member_map_)
# {4: <Fruit.Apple: 4>, 5: <Fruit.Orange: 5>, 6: <Fruit.Pear: 6>}
you can test if a value is in your Enum
against this map:
5 in Fruit._value2member_map_ # True
7 in Fruit._value2member_map_ # False
variant 2
if you do not want to rely on this feature this is an alternative:
values = [item.value for item in Fruit] # [4, 5, 6]
or (probably better): use a set
; the in
operator will be more efficient:
values = set(item.value for item in Fruit) # {4, 5, 6}
then test with
5 in values # True
7 in values # False
add has_value
to your class
you could then add this as a method to your class:
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
starting form python 3.9 (?) python offers IntEnum
. with these you could do this:
from enum import IntEnum
class Fruit(IntEnum):
Apple = 4
Orange = 5
Pear = 6
print(6 in iter(Fruit)) # True
note there is no need to create a list
; just iterating over iter(Fruit)
will do. again, if this is needed repeatedly it may be worth creating a set as above:
values = set(Fruit)
print(5 in values) # True
test for keys
if you want to test for the names (and not the values) i would use _member_names_
:
'Apple' in Fruit._member_names_ # True
'Mango' in Fruit._member_names_ # False
Just check whether it’s in 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
Building on what Reda Maachi started:
6 in Fruit.__members__.values()
returns True
7 in Fruit.__members__.values()
returns False
Originally, Don’t.
If you are using Enum, you can test for enum with
if isinstance(key, Fruit):
But otherwise, try.. is the pythonic way to test for enum. Indeed, for any break in the duck-typing paradigm.
The correct, and pythonic, way of testing for an int in an IntEnum is to give it a go and to catch a ValueError if there’s a failure.
Many of the solutions proposed above are actively deprecated and will be disallowed by 3.8 ( "DeprecationWarning: using non-Enums in containment checks will raise TypeError in Python 3.8" )
From Python 3.8 – Coerce it into a list.
After 3.8 or so, you can test by coercion of the enum into a list as follows (using IntEnum)
from enum import IntEnum
class Fruit(IntEnum):
Kiwi = 2
Apple = 4
Orange = 5
Pear = 6
for x in range(8):
print(f'{x}, {x in list(Fruit)}')
Which will print as follows
0, False
1, False
2, True
3, False
4, True
5, True
6, True
7, False
An EAFP version of the answer:
try:
Fruit(val)
return True
except ValueError:
return False
You could use __members__
special attribute to iterate over members:
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 Fruit.__members__.values()]
There’s another one liner solution nobody has mentioned yet:
is_value_in_fruit = any(f.value == value_to_check for f in Fruit)
Also, if you use IntEnum
instead of Enum
, (class Fruit(IntEnum)
) you can just do this
is_value_in_fruit = any(f == value_to_check for f in Fruit)
IntEnum
+ __members__
You could use IntEnum
and __members__
to achieve required behaviour:
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
+ list comprehension + .value
If you must/want stick to Enum
, you can do:
>>> 6 in [f.value for f in Fruit]
True
>>> 7 in [f.value for f in Fruit]
False
EAPF + ValueError
Or you can use easier to ask for forgiveness than permission method:
try:
Fruit(x)
except ValueError:
return False
else:
return True
There is a way to have all the enums be able to check if an item is present:
import enum
class MyEnumMeta(enum.EnumMeta):
def __contains__(cls, item):
return isinstance(item, cls) or item in [v.value for v in cls.__members__.values()]
class MyEnum(enum.Enum, metaclass=MyEnumMeta):
FOO = "foo"
BAR = "bar"
Now you can do an easy check:
>>> "foo" in MyEnum
True
It can even be made simpler if all the enum’s values will always be the same type — for example strings:
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"
Edit: As per the comments by @MestreLion, I’ve updated the first example to include the isinstance
check; without it the test fails if we’re checking an already instantiated enum value.
Edit: Yet another version, technically the most correct one:
import enum
class MyEnumMeta(enum.EnumMeta):
def __contains__(cls, item):
try:
cls(item)
except ValueError:
return False
else:
return True
class MyEnum(enum.Enum, metaclass=MyEnumMeta):
FOO = "foo"
BAR = "bar"
If the enum has many members, this approach can be faster because it doesn’t make a new list and stops walking the enum when the given value is found:
any(x.value == 5 for x in Fruit) # True
any(x.value == 7 for x in Fruit) # False
how about this?
from enum import Enum
class Fruit(Enum):
Apple = 4
Orange = 5
Pear = 6
has_apples = 4 in [n.value for n in Fruit]
This would let you also do:
has_apples = "Apple" in [n.name for n in Fruit]
I just convert an IntEnum to a list and test it normally:
from enum import IntEnum
class Foo(IntEnum):
ONE = 1
TWO = 2
THREE = 3
print(1 in list(Foo))
True
print(4 in list(Foo))
False
class MyEnumMixin:
raw_values = None # for IDE autocomplete
def __new__(cls, value):
if 'raw_values' not in cls.__dict__:
cls.raw_values = set()
cls.raw_values.add(value)
if cls.__bases__[0] is MyEnumMixin:
member = object().__new__(cls)
else:
member = super().__new__(cls, value)
member._value_ = value
return member
class MyEnum(MyEnumMixin, Enum):
FOO = 1
BAR = 'bar'
print(1 in MyEnum.raw_values)
print('bar' in MyEnum.raw_values)
class MyStrEnumMixin(MyEnumMixin, str):
pass
class MyStrEnum(MyStrEnumMixin, Enum):
FOO = 'foo'
BAR = 'bar'
print('foo' in MyStrEnum.raw_values)
print('bar' in MyStrEnum.raw_values)