I would like to extend Python Enums to support non-defined values.
My use case: I want to benefit from Enums (e.g., being able to address/compare known entries with speaking names) but I also want it to support unknown values. If there's no name behind the value, str should simply print the str representation of the value and comparisons should fail.
Let me give you a short example what I'd like to have:
from enum import Enum
class Foo(Enum):
A = 1
B = 2
C = 3
def __str__(self):
return self.name
print(Foo(1)) # prints 'A'
print(Foo(2)) # print 'B'
print(Foo(3)) # prints 'C'
print(Foo(1) == Foo.A) # prints 'true'
print(Foo(4)) # I'd expect '4'
print(Foo(123)) # I'd expect '123'
print(Foo(123) == Foo.A) # I'd expect False
Of course the last lines fail.
Is there a way to extend Enums, or is there maybe another easy pythonic way? (No third party libraries, please.)
Enums can't have multiple value per name.
Enum ValuesYou can assign different values to enum member. A change in the default value of an enum member will automatically assign incremental values to the other members sequentially.
Overview. The Java enum type provides a language-supported way to create and use constant values. By defining a finite set of values, the enum is more type safe than constant literal variables like String or int.
By definition, the enumeration member values are unique. However, you can create different member names with the same values. In this example, the Color enumeration has the RED , CRIMSON , and SALMON members with the same value 1.
Enums can be displayed as string or repr. 2. Enums can be checked for their types using type (). 3. “ name ” keyword is used to display the name of the enum member. 4. Enumerations are iterable. They can be iterated using loops
Enumerations are Python classes, and can have methods and special methods as usual. If we have this enumeration: >>> class Mood(Enum): ... FUNKY = 1 ...
Enums can be displayed as string or repr. 2. Enums can be checked for their types using type (). 3. “ name ” keyword is used to display the name of the enum member. 4. Enumerations are iterable. They can be iterated using loops 5. Enumerations support hashing. Enums can be used in dictionaries or sets.
To make your own Enum’s boolean evaluation depend on the member’s value add the following to your class: Enum classes always evaluate as True. If you give your Enum subclass extra methods, like the Planet class above, those methods will show up in a dir () of the member, but not of the class:
To make this work across multiple values of Python, particularly Python 2, you are going to need an external library: aenum
1.
from aenum import Enum, extend_enum
class Foo(Enum):
_order_ = 'A B C' # because Python 2 does not keep order
A = 1
B = 2
C = 3
def __str__(self):
return self.name
@classmethod
def _missing_(cls, value):
extend_enum(cls, str(value), (value, ))
return list(cls)[-1]
This dynamically adds the unknown value as an enum member and returns it.
1 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
Here's another answer that works in Python 3.6, 3.7 and 3.8.
It involves weird metaclass hackery, so be wary of that.
import enum
class TestMeta(enum.EnumMeta):
def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1):
if names is not None:
# the enum is being constructed via the functional syntax
return super().__call__(value, names=names, module=module, qualname=qualname, type=type, start=start)
try:
# attempt to get an enum member
return super().__call__(value, names=names, module=module, qualname=qualname, type=type, start=start)
except ValueError:
# no such member exists, but we don't care
return value
class Test(enum.Enum, metaclass=TestMeta):
A = 5
B = 6
print(Test(5), Test(6), Test(7))
This version works in Python 2.7, but requires a third-party library for enum
(see comments):
class TestMeta(enum.EnumMeta):
def __call__(cls, value, names=None, module=None, type=None, start=1):
if names is not None:
return enum.EnumMeta.__call__(cls, value, names, module, type, start)
try:
return enum.EnumMeta.__call__(cls, value, names, module, type, start)
except ValueError:
return value
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With