56

Using Python 3.4 I want to test whether an Enum class contains a member with a certain name.

Example:

class Constants(Enum):
    One = 1
    Two = 2
    Three = 3

print(Constants['One'])
print(Constants['Four'])

gives:

Constants.One
  File "C:\Python34\lib\enum.py", line 258, in __getitem__
    return cls._member_map_[name]
KeyError: 'Four'

I could catch the KeyError and take the exception as indication of existence but maybe there is a more elegant way?

5 Answers 5

87

You could use Enum.__members__ - an ordered dictionary mapping names to members:

In [12]: 'One' in Constants.__members__
Out[12]: True

In [13]: 'Four' in Constants.__members__
Out[13]: False
3
31

I would say this falls under EAFP (Easier to ask for forgiveness than permission), a concept that is relatively unique to Python.

Easier to ask for forgiveness than permission. This common Python coding style assumes the existence of valid keys or attributes and catches exceptions if the assumption proves false. This clean and fast style is characterized by the presence of many try and except statements. The technique contrasts with the LBYL style common to many other languages such as C.

This contrasts with LBYL (Look before you leap), which is what I think you want when you say you are looking for "a more elegant way."

Look before you leap. This coding style explicitly tests for pre-conditions before making calls or lookups. This style contrasts with the EAFP approach and is characterized by the presence of many if statements.

In a multi-threaded environment, the LBYL approach can risk introducing a race condition between “the looking” and “the leaping”. For example, the code, if key in mapping: return mapping[key] can fail if another thread removes key from mapping after the test, but before the lookup. This issue can be solved with locks or by using the EAFP approach.

Therefore based on the documentation, it is actually better to use try/except blocks for your problem.

TL;DR

Use try/except blocks to catch the KeyError exception.

3
  • 1
    Thanks for highlighting that try/except blocks are actually valid programming strategies in Python. I upvoted this answer too, but accepted the other because of personal taste. I rather do LBYL. Commented Apr 22, 2015 at 11:20
  • 6
    There are many cases where EAFP is the proper way to go, but I don't think this is one of them. The in operator exists precisely for the case of wanting to check if item in some_container. Commented Mar 9, 2016 at 9:00
  • 1
    @MisterMiyagi: True. In this case the container is the __members__ attribute. Commented Feb 10, 2018 at 15:35
13

In order to improve legibility, you can put these suggestions above as class method. For instance:

class Constants(Enum):
    One = 1
    Two = 2
    Three = 3

    @classmethod
    def has_key(cls, name):
        return name in cls.__members__ # solution above 1
        # return any(x for x in cls if x.name == name) # or solution above 2

In order to use:

In [6]: Constants.has_key('One')
Out[6]: True

In [7]: Constants.has_key('Four')
Out[7]: False
0
9

Could use the following to test if the name exists:

if any(x for x in Constants if x.name == "One"):
  # Exists
else:
  # Doesn't Exist

Of use x.value to test for the enum value:

if any(x for x in Constants if x.value == 1):
  # Exists
else:
  # Doesn't Exist
1
  • Note that this answer isn't correct. Iterating over the Constants Enum will skip over the alias values. This approach will incorrectly fail when attempting to use an Alias. See the Python Docs for the details. Commented Dec 24, 2023 at 4:03
2

Reading the source code for the Enum class:

def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
"""Either returns an existing member, or creates a new enum class.

So, based on the docstring notes, a Pythonic way of checking membership would be:

from enum import Enum

class TestEnum(Enum):
    TEST = 'test'

def enum_contains(enum_type, value):
    try:
        enum_type(value)
    except ValueError:
        return False
    return True

>>> enum_contains(TestEnum, 'value_doesnt_exist')
False
>>> enum_contains(TestEnum, 'test')
True

Not the answer you're looking for? Browse other questions tagged or ask your own question.