You can pass in a base type with mixin methods into the functional API, with the type
argument:
>>> import enum
>>> value_map = {'a': 1, 'b': 2}
>>> class DoubledEnum:
... @property
... def double_value(self):
... return self.value * 2
...
>>> MyEnum = enum.Enum('MyEnum', value_map, type=DoubledEnum)
>>> MyEnum.a.double_value
2
For a fully functional approach that never uses a class
statement, you can create the base mix-in with the type()
function:
DoubledEnum = type('DoubledEnum', (), {'double_value': property(double_value)})
MyEnum = enum.Enum('MyEnum', value_map, type=DoubledEnum)
You can also use enum.EnumMeta()
metaclass the same way, the way Python would when you create a class MyEnum(enum.Enum): ...
subclass:
- Create a class dictionary using the metaclass
__prepare__
hook
- Call the metaclass, passing in the class name, the bases (
(enum.Enum,)
here), and the class dictionary created in step 1.
The custom dictionary subclass that enum.EnumMeta
uses isn't really designed for easy reuse; it implements a __setitem__
hook to record metadata, but doesn't override the dict.update()
method, so we need to use a little care when using your value_map
dictionary:
import enum
def enum_with_extras(name, value_map, bases=enum.Enum, **extras):
if not isinstance(bases, tuple):
bases = bases,
if not any(issubclass(b, enum.Enum) for b in bases):
bases += enum.Enum,
classdict = enum.EnumMeta.__prepare__(name, bases)
for key, value in {**value_map, **extras}.items():
classdict[key] = value
return enum.EnumMeta(name, bases, classdict)
Then pass in double_value=property(double_value)
to that function (together with the enum name and value_map
dictionary):
>>> def double_value(self):
... return self.value * 2
...
>>> MyEnum = enum_with_extras('MyEnum', value_map, double_value=property(double_value))
>>> MyEnum.a
<MyEnum.a: 1>
>>> MyEnum.a.double_value
2
You are otherwise allowed to create subclasses of an enum without members (anything that's a descriptor is not a member, so functions, properties, classmethods, etc.), so you can define an enum without members first:
class DoubledEnum(enum.Enum):
@property
def double_value(self):
return self.value * 2
which is an acceptable base class for both in the functional API (e.g. enum.Enum(..., type=DoubledEnum)
) and for the metaclass approach I encoded as enum_with_extras()
.
Enum('MyEnum', {k: v * 2 for k, v in value_map.items()})
? – Egressself.value
on that method. Since there is no instance variable namedvalue
:/ – BallMyEnum = Enum('MyEnum', value_map)
, then I cannot define methods. But if I use thisclass MyEnum(Enum): ...
, then I must write all values explicitely, and cannot use my pre-defined dict. – Lodgings__prepare__
or__new__
magic methods of Enum and use my dict there. – Lodgings