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'
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
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.
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)
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
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
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")
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")
I generally use following code to have both functionality:
- ‘service’ in ENTITY_TYPES
- 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'
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.
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
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'
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
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.
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)
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
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
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")
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")
I generally use following code to have both functionality:
- ‘service’ in ENTITY_TYPES
- 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'
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.
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