Reputation: 1676
I've researched this and found similar answers, but they don't work in this case because auto()
is resolved earlier than __new__
is called and only if the whole value is _auto_null
.
Basically, what I want is the following:
class MyEnum(str, Enum):
one = '1 data'
two = '2 data'
def __new__(cls, data):
member = str.__new__(cls, <NEED NAME HERE>)
member.data = data
member._value_ = <NEED NAME HERE>
assert MyEnum.one == 'one'
assert MyEnum.one.value == 'one'
assert MyEnum.one.data == '1 data'
However, the name isn't passed into __new__
so there's nothing to fill in <NEED NAME HERE>
with.
Then, I tried to use auto()
in the following way:
class MyEnumBase(Enum):
def _generate_next_value_(name, start, count, last_values):
return name
class MyEnum(str, MyEnumBase):
one = '1 data'
two = '2 data'
def __new__(cls, data):
member = str.__new__(cls, auto())
member.data = data
member._value_ = auto()
assert MyEnum.one == 'one'
assert MyEnum.one.value == 'one'
assert MyEnum.one.data == '1 data'
This doesn't work because auto()
is only resolved when used as the sole value when defining a member, as in one = auto()
. So it seems it's not even possible to do one = auto(), '1 data'
and then take both of those parameters in the __new__
function.
class MyEnum(str, MyEnumBase):
one = auto(), '1 data'
two = auto(), '2 data'
def __new__(cls, name, data):
member = str.__new__(cls, name)
member.data = data
member._value_ = name
It seems my only option is to hardcode the name:
one = 'one', '1 data'
and define the __new__
function to take two params:
def __new__(cls, name, data):
...
Am I missing something? Is there a better way to do this? A better way to structure this information?
Upvotes: 5
Views: 570
Reputation: 69288
This is definitely advanced behavior, so to use it you'll need the aenum
1 library instead.
That code will look like:
from aenum import Enum
class MyEnum(str, Enum, init='value data'):
def __new__(cls, name, *args, **kwds):
obj = str.__new__(cls, name)
obj._value_ = name
return obj
def _generate_next_value_(name, start, count, last_values, *args, **kwds):
return (name, ) + args
one = '1 data'
two = '2 data'
The init
setting says what the given values should be used for; in this case:
value
for each memberdata
attribute for each memberAlso, if fewer items are given than init
says there should be, then _generate_next_value_
will be called in an attempt to provide the missing piece(s).
In this case, _generate_next_value
adds the name
to the given data
, which is then passed into __new__
.
__new__
uses the name
as it's value and ignores the rest
__init__
is automatically generated to handle the non-value items, so it sets the data
attribute
For those still using Python 2.7, or need their code to work for 2.7 as well 3.x, the above class should look like this (all changes are in the first block):
class MyEnum(str, Enum):
_order_ = 'one two' # only if order actually matters
_settings_ = AutoValue
_init_ = 'value data'
def __new__(cls, name, *args, **kwds):
obj = str.__new__(cls, name)
obj._value_ = name
return obj
def _generate_next_value_(name, start, count, last_values, *args, **kwds):
return (name, ) + args
one = '1 data'
two = '2 data'
1 Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
Upvotes: 7