Subclassing Python Enum with `auto()`, string values, and an extra property
Question:
I have an Enum subclass that I would like to do two things:
- Use the lowercased name of the enum as the value for each item and a property called
function
. I don’t want to have to type this out each time, so I’m using auto()
.
- Optionally, allow the user to pass a custom function name that overrides the lowercased name.
Here is the code I have come up with to do that:
from enum import Enum, auto
class LowerNameEnum(Enum):
def _generate_next_value_(name, start, count, last_values):
print(f"{name=}")
return name.lower()
class Task(str, LowerNameEnum):
def __new__(cls, value, function=None):
obj = str.__new__(cls, value)
obj._value_ = value
obj.function = function or "generic_function"
return obj
DO_SOMETHING = auto()
DO_SOMETHING_ELSE = auto()
DO_ANOTHER_THING = auto(), "do_it_do_it"
THING_4 = auto()
for t in Task:
print(t)
print(" ", t.value)
print(" ", t.function)
But when I run this, I get the following:
$ python myenum.py
name='DO_SOMETHING'
name='DO_SOMETHING_ELSE'
name='THING_4'
Task.DO_SOMETHING
do_something
generic_function
Task.DO_SOMETHING_ELSE
do_something_else
generic_function
Task.DO_ANOTHER_THING
<enum.auto object at 0x103000160>
do_it_do_it
Task.THING_4
thing_4
generic_function
When I try to use the auto()
value in combination with the extra argument, I get what looks like a __repr__
of an enum.auto
object instead of the lowercased item name. This is because _generate_next_value_
isn’t even called for this one, despite my having passed auto()
.
What am I doing wrong?
Update: Well, it turns out that value
in the __new__
method for the third item is an enum.auto
object. So that’s how it’s getting set as the value. I can use isinstance
to capture this, which is good. But then my question becomes: how do I find out the name of the item at this point so I can pass it to _generate_next_value_
? Or is there some better way to do this?
Answers:
Until this is fixed (in 3.12), you’ll need to use aenum
:
from aenum import Enum
...
class Task(str, LowerNameEnum):
...
DO_ANOTHER_THING = auto(function="do_it_do_it")
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
I have an Enum subclass that I would like to do two things:
- Use the lowercased name of the enum as the value for each item and a property called
function
. I don’t want to have to type this out each time, so I’m usingauto()
. - Optionally, allow the user to pass a custom function name that overrides the lowercased name.
Here is the code I have come up with to do that:
from enum import Enum, auto
class LowerNameEnum(Enum):
def _generate_next_value_(name, start, count, last_values):
print(f"{name=}")
return name.lower()
class Task(str, LowerNameEnum):
def __new__(cls, value, function=None):
obj = str.__new__(cls, value)
obj._value_ = value
obj.function = function or "generic_function"
return obj
DO_SOMETHING = auto()
DO_SOMETHING_ELSE = auto()
DO_ANOTHER_THING = auto(), "do_it_do_it"
THING_4 = auto()
for t in Task:
print(t)
print(" ", t.value)
print(" ", t.function)
But when I run this, I get the following:
$ python myenum.py
name='DO_SOMETHING'
name='DO_SOMETHING_ELSE'
name='THING_4'
Task.DO_SOMETHING
do_something
generic_function
Task.DO_SOMETHING_ELSE
do_something_else
generic_function
Task.DO_ANOTHER_THING
<enum.auto object at 0x103000160>
do_it_do_it
Task.THING_4
thing_4
generic_function
When I try to use the auto()
value in combination with the extra argument, I get what looks like a __repr__
of an enum.auto
object instead of the lowercased item name. This is because _generate_next_value_
isn’t even called for this one, despite my having passed auto()
.
What am I doing wrong?
Update: Well, it turns out that value
in the __new__
method for the third item is an enum.auto
object. So that’s how it’s getting set as the value. I can use isinstance
to capture this, which is good. But then my question becomes: how do I find out the name of the item at this point so I can pass it to _generate_next_value_
? Or is there some better way to do this?
Until this is fixed (in 3.12), you’ll need to use aenum
:
from aenum import Enum
...
class Task(str, LowerNameEnum):
...
DO_ANOTHER_THING = auto(function="do_it_do_it")
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.