How to check if string exists in Enum of strings? - python

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'

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

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

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

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)

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

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

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.

Related

How to make a polymorphic dataclass constructor method

I have 3 dataclass objects say:
class Message1:
def __init__(a):
...
class Message2:
def __init__(d,e,f):
...
class Message3:
def __init__(g,i):
...
For these 3 messages I want to make a factory type method which can return one of the three objects if it succeeds and if not it should return either the one it identified as the correct message to be created but failed at creation or it should notify the user that it could not create any of the messages. Are there any OOP patterns for this?
My initial thought was to do a:
def factory_method(**parameters):
try:
Message1(**parameters)
except TypeError:
try:
Message2(**parameters)
except:
try:
Message3(**parameters)
except:
print("Could not deduce message type")
My issue with this idea is that:
It's not a dynamically scalable solution, with each new message class I introduce I need to add a new try catch block
If the whole nested block structure fails, I have no feedback as to why, was the parameters correct for one of the message but wrong value, or was it plain gibberish?
I realize this might be a bit opinion based on what the best outcome is. At the same time it might be the solution is not too elegant and the simplest way is to just tell the factory_method what kind of message to initialize. Any suggestions or ideas would be appreciated.
If you can't join them all in a single class and you can't point a call to a single class, i would match the arguments to the posible class. To make it work a type hint and a "proxy" class is required. This example asumes that any of the classes wont contain a __init__(*args, **kwargs), and to add a new class you just add it to Message.msg_cls, you can eval the global scope if you don't want to add manually each class.
class Message1:
def __init__(self, a: int, alt=None, num=10):
print('Message 1')
class Message2:
def __init__(self, d: str, e: str, f: int):
print('Message 2')
class Message3:
def __init__(self, g: int, i: any):
print('Message 3')
class Message:
msg_cls = (
Message1,
Message2,
Message3
)
#staticmethod
def eq_kwargs(cls, kwargs):
cls_kwargs = cls.__init__.__defaults__
if cls_kwargs is None:
if len(kwargs) > 0:
return False
else:
return True
cls_astr = cls.__init__.__code__
kw_types = [type(t) for t in cls_kwargs]
for k in kwargs:
if k in cls_astr.co_varnames:
if type(kwargs[k]) in kw_types:
kw_types.remove(type(kwargs[k]))
else:
if type(None) in kw_types:
kw_types.remove(type(None))
else:
return False
else:
return False
return True
#staticmethod
def eq_args(cls, args):
cls_args = cls.__init__.__annotations__
if len(cls_args) != len(args):
return False
for a, b in zip(args, cls_args):
if type(a) != cls_args[b] and cls_args[b] != any:
return False
return True
def __new__(cls, *args, **kwargs):
for mc in Message.msg_cls:
if Message.eq_args(mc, args):
if Message.eq_kwargs(mc, kwargs):
return mc(*args, **kwargs)
raise ValueError('Message.__new__, no match')
if __name__ == '__main__':
ms_1_a = Message(1, alt='a')
ms_1_b = Message(2, alt='a', num=5)
ms_2 = Message('X', 'Y', 5)
ms_3_a = Message(1, [1, 4])
ms_3_b = Message(2, Message(10))

Pythonic way of using enum to select and run a particular sub method?

I have a function that will be called by an end user, and this function accepts an int argument from 1 to 3 inclusive. Depending on the int, I want the function to run a seperate sub method. I would usually do this with if statements as below, but I am looking for a cleaner / more pythonic way to do this using Enum.
def user_function(user_arg: int) -> float:
if user_arg == 1:
return sub_method_1()
elif user_arg == 2:
return sub_method_2()
elif user_arg == 3:
return sub_mthod_3()
print(f"Error: user_arg must be in [1,2,3]. Supplied argument: {user_arg}")
Ideally the function would look something like
from enum import Enum
class UserMethod(Enum):
FIRST_METHOD = 1
SECOND_METHOD = 2
THIRD_METHOD = 3
def choose_method(self):
#... use enum to choose from the 3 sub methods ...
# This function is now much cleaner
def user_function(user_arg: int) -> float:
um = UserMethod(user_arg)
return um.choose_method()
I am confused on how to use Enum on how to do this, or if this is even the best way?
The cleanest way to do this at the moment is to use the aenum library1:
from aenum import Enum
class UserMethod(Enum):
#
_init_ = 'value method'
#
def __call__(self, *args, **kwds):
return self.method(*args, **kwds)
#
def first(blah):
return blah
#
def second(spam=2):
return spam
#
def third(this, that):
return this, that
#
FIRST_METHOD = 1, first
SECOND_METHOD = 2, second
THIRD_METHOD = 3, third
Each method is stored on the enum member itself, and calling the member passes the call to the appropriate function.
--> list(UserMethod)
[<UserMethod.FIRST_METHOD: 1>, <UserMethod.SECOND_METHOD: 2>,
<UserMethod.THIRD_METHOD: 3>]
--> UserMethod.THIRD_METHOD("hello", "world")
('hello', 'world')
and
# This function is now much cleaner
def user_function(user_arg: int) -> float:
return UserMethod(user_arg).method
1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
You can keep it even simpler (that is, without relying on Python Enums):
def user_function(user_arg: int) -> float:
assert user_arg in [1,2,3], \
f"Error: user_arg must be in [1,2,3]. Supplied argument: {user_arg}"
return [sum_method_1,
sub_method_2,
sub_method_3][user_arg-1]()
On your method choose_method you can get the selected Enum using self like this:
def choose_method(self):
print(self) # Will print the Enum atributte
With this you can create a dict pointing to another methods;
def choose_method(self):
methods = {
UserMethod.FIRST_METHOD: self.sub_method_one,
UserMethod.SECOND_METHOD: self.sub_method_two,
UserMethod.THIRD_METHOD: self.sub_method_three,
}
return methods.get(self)()
def sub_method_one(self):
print("Method 1")
def sub_method_two(self):
print("Method 2")
def sub_method_three(self):
print("Method 3")
Using enums would not be my first impulse - I would argue that it wouldn't be pythonic to use one in this scenario, and I don't think you can justify using an enum. You'll probably just want to map the user's input to a function using a dictionary:
def first_function():
print("In first!")
def second_function():
print("In second!")
def third_function():
print("In third!")
functions = {
1: first_function,
2: second_function,
3: third_function
}
functions[1]()
Output:
In first!
>>>
In this case, the keys are integers, but they could be strings, too - or anything. I also just hardcoded the 1 in functions[1](), but you get the idea.

Convert string to Enum in Python

What's the correct way to convert a string to a corresponding instance of an Enum subclass? Seems like getattr(YourEnumType, str) does the job, but I'm not sure if it's safe enough.
As an example, suppose I have an enum like
class BuildType(Enum):
debug = 200
release = 400
Given the string 'debug', how can I get BuildType.debug as a result?
This functionality is already built in to Enum:
>>> from enum import Enum
>>> class Build(Enum):
... debug = 200
... build = 400
...
>>> Build['debug']
<Build.debug: 200>
The member names are case sensitive, so if user-input is being converted you need to make sure case matches:
an_enum = input('Which type of build?')
build_type = Build[an_enum.lower()]
Another alternative (especially useful if your strings don't map 1-1 to your enum cases) is to add a staticmethod to your Enum, e.g.:
class QuestionType(enum.Enum):
MULTI_SELECT = "multi"
SINGLE_SELECT = "single"
#staticmethod
def from_str(label):
if label in ('single', 'singleSelect'):
return QuestionType.SINGLE_SELECT
elif label in ('multi', 'multiSelect'):
return QuestionType.MULTI_SELECT
else:
raise NotImplementedError
Then you can do question_type = QuestionType.from_str('singleSelect')
def custom_enum(typename, items_dict):
class_definition = """
from enum import Enum
class {}(Enum):
{}""".format(typename, '\n '.join(['{} = {}'.format(k, v) for k, v in items_dict.items()]))
namespace = dict(__name__='enum_%s' % typename)
exec(class_definition, namespace)
result = namespace[typename]
result._source = class_definition
return result
MyEnum = custom_enum('MyEnum', {'a': 123, 'b': 321})
print(MyEnum.a, MyEnum.b)
Or do you need to convert string to known Enum?
class MyEnum(Enum):
a = 'aaa'
b = 123
print(MyEnum('aaa'), MyEnum(123))
Or:
class BuildType(Enum):
debug = 200
release = 400
print(BuildType.__dict__['debug'])
print(eval('BuildType.debug'))
print(type(eval('BuildType.debug')))
print(eval(BuildType.__name__ + '.debug')) # for work with code refactoring
My Java-like solution to the problem. Hope it helps someone...
from enum import Enum, auto
class SignInMethod(Enum):
EMAIL = auto(),
GOOGLE = auto()
#classmethod
def value_of(cls, value):
for k, v in cls.__members__.items():
if k == value:
return v
else:
raise ValueError(f"'{cls.__name__}' enum not found for '{value}'")
sim = SignInMethod.value_of('EMAIL')
assert sim == SignInMethod.EMAIL
assert sim.name == 'EMAIL'
assert isinstance(sim, SignInMethod)
# SignInMethod.value_of("invalid sign-in method") # should raise `ValueError`
An improvement to the answer of #rogueleaderr :
class QuestionType(enum.Enum):
MULTI_SELECT = "multi"
SINGLE_SELECT = "single"
#classmethod
def from_str(cls, label):
if label in ('single', 'singleSelect'):
return cls.SINGLE_SELECT
elif label in ('multi', 'multiSelect'):
return cls.MULTI_SELECT
else:
raise NotImplementedError
Change your class signature to this:
class BuildType(str, Enum):
Since MyEnum['dontexist'] will result in error KeyError: 'dontexist', you might like to fail silently (eg. return None). In such case you can use the following static method:
class Statuses(enum.Enum):
Unassigned = 1
Assigned = 2
#staticmethod
def from_str(text):
statuses = [status for status in dir(
Statuses) if not status.startswith('_')]
if text in statuses:
return getattr(Statuses, text)
return None
Statuses.from_str('Unassigned')
class LogLevel(IntEnum):
critical = logging.CRITICAL
fatal = logging.FATAL
error = logging.ERROR
warning = logging.WARNING
info = logging.INFO
debug = logging.DEBUG
notset = logging.NOTSET
def __str__(self):
return f'{self.__class__.__name__}.{self.name}'
#classmethod
def _missing_(cls, value):
if type(value) is str:
value = value.lower()
if value in dir(cls):
return cls[value]
raise ValueError("%r is not a valid %s" % (value, cls.__name__))
Example:
print(LogLevel('Info'))
print(LogLevel(logging.WARNING))
print(LogLevel(10)) # logging.DEBUG
print(LogLevel.fatal)
print(LogLevel(550))
Output:
LogLevel.info
LogLevel.warning
LogLevel.debug
LogLevel.critical
ValueError: 550 is not a valid LogLevel
I just want to notify this does not work in python 3.6
class MyEnum(Enum):
a = 'aaa'
b = 123
print(MyEnum('aaa'), MyEnum(123))
You will have to give the data as a tuple like this
MyEnum(('aaa',))
EDIT:
This turns out to be false. Credits to a commenter for pointing out my mistake

How to assert that an iterable is not empty on Unittest?

After submitting queries to a service, I get a dictionary or a list back and I want to make sure it's not empty. I using Python 2.7.
I am surprised of not having any assertEmpty method for the unittest.TestCase class instance.
The existing alternatives just don't look right:
self.assertTrue(bool(d))
self.assertNotEqual(d,{})
self.assertGreater(len(d),0)
Is this kind of a missing method in the Python unittest framework? If yes, what would be the most pythonic way to assert that an iterable is not empty?
Empty lists/dicts evaluate to False, so self.assertTrue(d) gets the job done.
Depends exactly what you are looking for.
If you want to make sure the object is an iterable and it is not empty:
# TypeError: object of type 'NoneType' has no len()
# if my_iterable is None
self.assertTrue(len(my_iterable))
If it is OK for the object being tested to be None:
self.assertTrue(my_maybe_iterable)
"Falsy" values in Python
A falsy (sometimes written falsey) value is a value that is considered false when encountered in a Boolean context.
According to the official doc, the following built-in types evaluate to false:
constants defined to be false: None and False.
zero of any numeric type: 0, 0.0, 0j, Decimal(0), Fraction(0, 1)
empty sequences and collections: '', (), [], {}, set(), range(0)
Therefore, it's possible to check for
non-emptiness with assertTrue() and for
emptiness with assertFalse().
(The official doc has a full list of all available assert methods.)
Clean Code
All those assertTrue() and assertFalse() calls are kind of misleading as we wanted to check for emptiness and one needs to know which types evaluate to false to properly understand what's happening in the test.
So, for the sake of clean code and for better readability, we can simply define our own assertEmpty() and assertNotEmpty() methods like so:
def assertEmpty(self, obj):
self.assertFalse(obj)
def assertNotEmpty(self, obj):
self.assertTrue(obj)
Maybe:
self.assertRaises(StopIteration, next(iterable_object))
All the credit for this goes to winklerrr, I am just extending his idea: have importable mixins for when you need assertEmpty or assertNotEmpty:
class AssertEmptyMixin( object ):
def assertEmpty(self, obj):
self.assertFalse(obj)
class AssertNotEmptyMixin( object ):
def assertNotEmpty(self, obj):
self.assertTrue(obj)
Caveat, mixins should go on the left:
class MyThoroughTests( AssertNotEmptyMixin, TestCase ):
def test_my_code( self ):
...
self.assertNotEmpty( something )
Based on #winklerr's answer and #Merk's comment, I extended the idea for checking whether the given object is a Container in the first place.
from typing import Container
def assertContainerEmpty(self, obj: Container) -> None:
"""Asserts whether the given object is an empty container."""
self.assertIsInstance(obj, Container)
self.assertFalse(obj)
def assertContainerNotEmpty(self, obj: Container) -> None:
"""Asserts whether the given object is a non-empty container."""
self.assertIsInstance(obj, Container)
self.assertTrue(obj)
This means that assertEmpty and assertNotEmpty will always fail if the given object is e.g. a float, or an instance of an user-defined class - no matter if it would properly evaluate to True/False.
A slightly different answer to those already proposed... If specific named assertions are absolutely required, you could subclass TestCase and add methods for new assertions there.
from pathlib import Path
from typing import Container
from unittest import TestCase
class BaseTestCase(TestCase):
def assertIsFile(self, path: str, msg: str=None) -> None:
default_msg = 'File does not exist: {0}'.format(path)
msg = msg if msg is not None else default_msg
if not Path(path).resolve().is_file():
raise AssertionError(msg)
def assertIsEmpty(self, obj: Container, msg: str=None) -> None:
default_msg = '{0} is not empty.'.format(obj)
msg = msg if msg is not None else default_msg
self.assertIsInstance(obj, Container, '{0} is not a container.'.format(obj))
if len(obj) > 0:
raise AssertionError(msg)
def assertIsNotEmpty(self, obj: Container, msg: str=None) -> None:
default_msg = '{0} is empty.'.format(obj)
msg = msg if msg is not None else default_msg
self.assertIsInstance(obj, Container, '{0} is not a container.'.format(obj))
if obj is None or len(obj) == 0:
raise AssertionError(msg)
And then subclass the new BaseTestCase class to use the new assertion methods.
class TestApplicationLoadBalancer(_BaseTestCase):
def setUp(self) -> None:
# These assertions will fail.
self.assertIsFile('does-not-exist.txt')
self.assertIsEmpty(['asdf'])
self.assertIsNotEmpty([])
Just like the built-in unittest assertions, you can pass an error message to these if desired.
class TestApplicationLoadBalancer(_BaseTestCase):
def setUp(self) -> None:
# These assertions will fail.
self.assertIsFile('does-not-exist.txt', 'Foo')
self.assertIsEmpty(['asdf'], 'Bar')
self.assertIsNotEmpty([], 'Baz')

Is there a better way to get a named series of constants (enumeration) in Python? [duplicate]

This question already has answers here:
How can I represent an 'Enum' in Python?
(43 answers)
Closed 9 years ago.
Just looking at ways of getting named constants in python.
class constant_list:
(A_CONSTANT, B_CONSTANT, C_CONSTANT) = range(3)
Then of course you can refer to it like so:
constant_list.A_CONSTANT
I suppose you could use a dictionary, using strings:
constant_dic = {
"A_CONSTANT" : 1,
"B_CONSTANT" : 2,
"C_CONSTANT" : 3,}
and refer to it like this:
constant_dic["A_CONSTANT"]
My question, then, is simple. Is there any better ways of doing this? Not saying that these are inadequate or anything, just curious - any other common idioms that I've missed?
Thanks in advance.
For 2.3 or after:
class Enumerate(object):
def __init__(self, names):
for number, name in enumerate(names.split()):
setattr(self, name, number)
To use:
codes = Enumerate('FOO BAR BAZ')
codes.BAZ will be 2 and so on.
If you only have 2.2, precede this with:
from __future__ import generators
def enumerate(iterable):
number = 0
for name in iterable:
yield number, name
number += 1
(This was taken from here)
I find the enumeration class recipe (Active State, Python Cookbook) to be very effective.
Plus it has a lookup function which is nice.
Pev
An alternative construction for constant_dic:
constants = ["A_CONSTANT", "B_CONSTANT", "C_CONSTANT"]
constant_dic = dict([(c,i) for i, c in enumerate(constants)])
The following acts like a classisc "written in stone" C enum -- once defined, you can't change it, you can only read its values. Neither can you instantiate it. All you have to do is "import enum.py" and derive from class Enum.
# this is enum.py
class EnumException( Exception ):
pass
class Enum( object ):
class __metaclass__( type ):
def __setattr__( cls, name, value ):
raise EnumException("Can't set Enum class attribute!")
def __delattr__( cls, name ):
raise EnumException("Can't delete Enum class attribute!")
def __init__( self ):
raise EnumException("Enum cannot be instantiated!")
This is the test code:
# this is testenum.py
from enum import *
class ExampleEnum( Enum ):
A=1
B=22
C=333
if __name__ == '__main__' :
print "ExampleEnum.A |%s|" % ExampleEnum.A
print "ExampleEnum.B |%s|" % ExampleEnum.B
print "ExampleEnum.C |%s|" % ExampleEnum.C
z = ExampleEnum.A
if z == ExampleEnum.A:
print "z is A"
try:
ExampleEnum.A = 4
print "ExampleEnum.A |%s| FAIL!" % ExampleEnum.A
except EnumException:
print "Can't change Enum.A (pass...)"
try:
del ExampleEnum.A
except EnumException:
print "Can't delete Enum.A (pass...)"
try:
bad = ExampleEnum()
except EnumException:
print "Can't instantiate Enum (pass...)"
This is the best one I have seen: "First Class Enums in Python"
http://code.activestate.com/recipes/413486/
It gives you a class, and the class contains all the enums. The enums can be compared to each other, but don't have any particular value; you can't use them as an integer value. (I resisted this at first because I am used to C enums, which are integer values. But if you can't use it as an integer, you can't use it as an integer by mistake so overall I think it is a win.) Each enum is a unique object. You can print enums, you can iterate over them, you can test that an enum value is "in" the enum. It's pretty complete and slick.
In Python, strings are immutable and so they are better for constants than numbers. The best approach, in my opinion, is to make an object that keeps constants as strings:
class Enumeration(object):
def __init__(self, possibilities):
self.possibilities = set(possibilities.split())
def all(self):
return sorted(self.possibilities)
def __getattr__(self, name):
if name in self.possibilities:
return name
raise AttributeError("Invalid constant: %s" % name)
You could then use it like this:
>>> enum = Enumeration("FOO BAR")
>>> print enum.all()
['BAR', 'FOO']
>>> print enum.FOO
FOO
>>> print enum.FOOBAR
Traceback (most recent call last):
File "enum.py", line 17, in <module>
print enum.FOOBAR
File "enum.py", line 11, in __getattr__
raise AttributeError("Invalid constant: %s" % name)
AttributeError: Invalid constant: FOOBAR

Categories

Resources