177

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)?

2
  • 4
    Using try/except in Python is absolutely okay (although I admit that I had a hard time to get it). So, what is the reason for you not to use it? Commented Oct 12, 2021 at 13:23
  • 1
    what is the reason for you not to use it? I Could imagine readability. Commented Dec 4, 2024 at 4:56

17 Answers 17

249

UPDATE

my answer is outdated. for newer python versions see Filip Poplewski's answer.


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
Sign up to request clarification or add additional context in comments.

9 Comments

I personally prefer the second solution. Thank you.
Either advice is not particularly memory- or time-efficient, although also not very heavy in these terms. In my case, I am working with the HTTPStatus enum from the standard library which as of now contains 57 entries. I would never process a whole list of values from an enumeration, and caching them is something I'd like to avoid.
@none if you are worried about speed you could create a set for the lookup. that would use some additional memory but not a significant amount...
@PedroLourenço: circumventing this is the whole point of the OP. This answer (and many others) work fine in 3.8 even for non-Enums
Outdated solution, example for newer python versions
|
47

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"

11 Comments

A works, B works not, C works. Counter Example: class A(enum.Enum): ABC = 123. 123 in A.__members__.values() results in False. (Python 3.6)
I'm not sure what you mean by "B works not"; all three examples work fine. Also, I'm not sure what your "counter example" means; this is exactly the expected behaviour.
@berislav-lopac The counter example is meant for the B case. BUT @avans it is wrong, missing the critical constriction that the MyEnum only contains of str values, in case B.
Could you please expand the answer explaining why the B case requires values to be homogeneous? Is it because case B fails if item is an instance of A? So what it actually requires is that A(x) == x ? Wow, that's subtle, and it took a while for me to figure it out (that is, if I got it right)
@BerislavLopac: method B does not require the additional isinstance() check: as its members are also instances of str, instances of MyEnum will pass the item in cls.__members__.values() check. And you can use your original simplification item in cls, it will work for typed Enums.
|
25

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

4 Comments

The question is asking to test for an int value, not a string.
Is there no member in Enum that contains the values? Isn't that strange?
This goes in the opposite direction from what the question asked.
This doesn't work for values! (it's for "keys") answerer probably had Apple = "Apple", so he had the same letters & lettercase used for both key and value ... if you'd have APPLE = "Apple" then .__members__ will result in {'APPLE': <Fruit.APPLE: 'Apple'>, 'GRAPE': <Fruit.GRAPE: 'Grape'>}
20

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

1 Comment

this should be much higher... simple and effective, thanks!
18

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

2 Comments

Perhaps this only works for IntEnum. I needed to do the following to get the member values as a list [m.value for m in MyEnum.__members__.values()]
@ryanjdillon No, this works for all enum types. Since EnumMeta (which is renamed to EnumType since Python 3.11) implements __iter__, all enum types are iterable and can be converted to a list. doc
17

In newer versions of Python (3.12+) it is simple as that:

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

print('6 is a Fruit number' if 6 in Fruit else '6 is not a Fruit number')
print('7 is a Fruit number' if 7 in Fruit else '7 is not a Fruit number')

or with usage of the functional syntax

Fruit = Enum('Fruit', [('Apple', 4), ('Orange', 5), ('Pear', 6)])
print('6 is a Fruit number' if 6 in Fruit else '6 is not a Fruit number')
print('7 is a Fruit number' if 7 in Fruit else '7 is not a Fruit number')

output:

6 is a Fruit number
7 is not a Fruit number

3 Comments

Can you specify what "newer versions of Python" are? 3.what+?
According to documentation 3.12+, but I didn't checked that out. docs.python.org/3/library/…
Tested it, 3.11 doesn't support it and raised the TypeError, so it is indeed 3.12+
14

Building on what Reda Maachi started:

6 in Fruit.__members__.values() 

returns True

7 in Fruit.__members__.values()  

returns False

2 Comments

This looked like a very clean way. Unfortunately, when I tested this (at least in Py3.7) it doesn't work like that. Fruit.__members__.values() evaluates to odict_values([<Fruit.Apple: 4>, <Fruit.Orange: 5>, <Fruit.Pear: 6>]), and both the tests above for 6 and 7 return False for me. But this returns true: >>> Fruit.Orange in Fruit.__members__.values() True
I'd like to point out that this works as written for IntEnum only.
8

An EAFP version of the answer:

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

Comments

7

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()]

Comments

6

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

3 Comments

This solution is actually wrong. Since OP asked how to check if a value was in an enum, such as 6, or 7. This will always return false, since none of the members of the enum are actually equal to 6 or 7. Try it out.
But this does do what most people of googling for, how to check if a value is an enum, upvoted! Thank you!
The DeprecationWarning mentioned in this answer will be outdated starting from Python 3.12. Python 3.11 now includes information notifying of the upcoming change in 3.12. "DeprecationWarning: in 3.12 contains will no longer raise TypeError, but will return True or False depending on whether the value is a member or the value of a member".
5

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

1 Comment

_value2member_map_ is an undocumented feature, so it's best to avoid using it.
4

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) 

Comments

3

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]

Comments

2

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

Comments

1

I find the cleanest and most familiar way to do this is with try/except:

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

# If you want it in function form
def is_fruit(x):
    try:
        Fruit(x)
    except ValueError:
        return False
    else:
        return True

Comments

0

Summarizing all the above and creating a new class that can be used instead of simple Enum:

from enum import Enum, EnumMeta
    
    
class BetterEnumMeta(EnumMeta):
    def __contains__(cls, item):
        if isinstance(item, Enum):
            return item.__class__ is cls
    
        return any(item == member.value for member in cls.__members__.values())
    
    
class BetterEnum(Enum, metaclass=BetterEnumMeta):
    ...

Having the code in the better_enum.py one can just import the BetterEnum instead of the enum.Enum and use it as usual with extended "in" checks:

from better_enum import BetterEnum

class Fruit(int, BetterEnum):
    Apple = 4
    Orange = 5
    Pear = 6

class Colors(str, BetterEnum):
    red = "red"
    blue = "blue"
    black = "black"

With this subclassing, you'll have a simple way to check if your enum has an arbitrary value:

6 in Fruit # returns True
"Banana" in Fruit # returns False
"blue" in Colors # returns True
"Banana" in Colors # returns False

Of course, you can use the BetterEnum instead of the enum.Enum without changing your class definitions with:

from better_enum import BetterEnum as Enum

class Fruit(Enum):
    ...

Comments

-1
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)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.