How to check if string exists in Enum of strings?

Question:

I have created the following Enum:

from enum import Enum

class Action(str, Enum):
    NEW_CUSTOMER = "new_customer"
    LOGIN = "login"
    BLOCK = "block"

I have inherited from str, too, so that I can do things such as:

action = "new_customer"
...
if action == Action.NEW_CUSTOMER:
    ...

I would now like to be able to check if a string is in this Enum, such as:

if "new_customer" in Action:
    ....

I have tried adding the following method to the class:

def __contains__(self, item):
    return item in [i for i in self]

However, when I run this code:

print("new_customer" in [i for i in Action])
print("new_customer" in Action)

I get this exception:

True
Traceback (most recent call last):
  File "/Users/kevinobrien/Documents/Projects/crazywall/utils.py", line 24, in <module>
    print("new_customer" in Action)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/enum.py", line 310, in __contains__
    raise TypeError(
TypeError: unsupported operand type(s) for 'in': 'str' and 'EnumMeta'
Asked By: KOB

||

Answers:

You can check if the enum contains a value by calling it:

>>> Action('new_customer')
Action.NEW_CUSTOMER

If the object you pass in is not guarantee to be in the enum, you can use a try block to capture the resulting ValueError. E.g.,

def is_action(obj):
    try:
        Action(obj)
    except ValueError:
        return False
    return True
Answered By: Mad Physicist

Since Action is a derived class of Enum, we can use the fact that Enum has a member called _value2member_map_.

value2member_map is a private attribute (i.e. Internally in CPython) tthat maps values to names(will only work for hashable values though). However, it’s not a good idea to rely on private attributes as they can be changed anytime.

Reference

We get the following:

if "new_customer" in Action._value2member_map_:  # works

which is close to your desired:

if "new_customer" in Action:  # doesn't work (i.e. TypeError)
Answered By: DarrylG

I just bumped into this problem today (2020-12-09); I had to change a number of subpackages for Python 3.8.

Perhaps an alternative to the other solutions here is the following, inspired by the excellent answer here to a similar question, as well as @MadPhysicist’s answer on this page:

from enum import Enum, EnumMeta


class MetaEnum(EnumMeta):
    def __contains__(cls, item):
        try:
            cls(item)
        except ValueError:
            return False
        return True    


class BaseEnum(Enum, metaclass=MetaEnum):
    pass


class Stuff(BaseEnum):
    foo = 1
    bar = 5

Tests (python >= 3.7; tested up to 3.10):

>>> 1 in Stuff
True

>>> Stuff.foo in Stuff
True

>>> 2 in Stuff
False

>>> 2.3 in Stuff
False

>>> 'zero' in Stuff
False
Answered By: Pierre D

Given an enum of languages

class Language(enum.Enum):
    en = 'en'
    zh = 'zh'

    @classmethod
    def has_member_key(cls, key):
        return key in cls.__members__

print(Language.has_member_key('tu')) => False
print(Language.has_member_key('en')) => True
Answered By: Shady Smaoui

You can also check contains in enum by brackets like in dict

class Action(Enum):
    NEW_CUSTOMER = 1
    LOGIN = 2
    BLOCK = 3

action = 'new_customer'
try:
    action = Action[action.upper()]
    print("action type exists")
except KeyError:
    print("action type doesn't exists")
Answered By: Pavivin

You can use hasattr instead of in if you can get Enum member name with what you have.

action = "new_customer"
enum_member_name = action.upper()

if hasattr(Action, enum_member_name):
    print(f"{action} in Action")
else:
    print(f"{action} not in Action")
Answered By: alercelik

I generally use following code to have both functionality:

  1. ‘service’ in ENTITY_TYPES
  2. ENTITY_TYPES.SERVICE in ENTITY_TYPES
from enum import Enum, EnumMeta
from typing import Any

class EnumeratorMeta(EnumMeta):

    def __contains__(cls, member: Any):
        if type(member) == cls:
            return EnumMeta.__contains__(cls, member)
        else:
            try:
                cls(member)
            except ValueError:
                return False
            return True


class Enumerator(Enum, metaclass=EnumeratorMeta):
    pass


class ENTITY_TYPES(Enumerator):
    SERVICE: str = 'service'
    CONFIGMAP: str = 'configmap'
Answered By: Hubert Tarnacki

I want to keep my enum classes generic (without altering internal functionality):

def clean_class_dict(class_dict):
    return_dict = dict(class_dict)
    for key in list(return_dict.keys()):
        if key[0] == "_":
            del return_dict[key]
    return return_dict

def item_in_enum_titles(item: str, enum: Enum):
    enum_dict = clean_class_dict(enum.__dict__)
    if item in enum_dict.keys():
        return True
    else:
        return False

I convert my enum to a dict and remove all the private functions and variables.

Answered By: bieboebap

Here’s a simple way to check if a string value belongs to an Enum. Just thought I’d include it here for completeness:

from enum import Enum

class Action(Enum):
    NEW_CUSTOMER = "new_customer"
    LOGIN = "login"

actions = [a.value for a in Action]

# client code
returns_true = 'login' in actions
returns_false = 'Not an action' in actions

Answered By: DV82XL
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.