Reputation: 861
I'm trying to create a custom enumerator that can replace an int, but has additional fields.
from enum import IntEnum
class MD_Fields(IntEnum):
ACCOUNT = (0, "Account", True)
M_DESCRIPT = (4, "Description", False)
def __new__(cls, value: int, description: str, identifier: bool):
obj = int.__new__(cls, value)
obj.description = description
obj.identifier = identifier
return obj
if __name__ == '__main__':
print(MD_Fields.M_DESCRIPT)
However, this code raises the following problem:
Traceback (most recent call last):
File ".../JetBrains/PyCharmCE2022.3/scratches/scratch.py", line 3, in <module>
class MD_Fields(IntEnum):
File "/usr/lib/python3.7/enum.py", line 223, in __new__
enum_member._value_ = member_type(*args)
TypeError: int() takes at most 2 arguments (3 given)
I don't understand what's happening.
(I didn't find a meaningful definition of int.__new__
)
Upvotes: 3
Views: 338
Reputation: 1118
You were close - it was only missing the obj._value_ = value
assignment, which Enum
needs:
from enum import IntEnum
class MD_Fields(IntEnum):
ACCOUNT = (0, "Account", True)
M_DESCRIPT = (4, "Description", False)
def __new__(cls, value: int, description: str, identifier: bool):
obj = int.__new__(cls, value)
obj._value_ = value
obj.description = description
obj.identifier = identifier
return obj
After adding that, it works as intended:
>>> for member in MD_Fields:
... member, int(member), member.description, member.identifier
...
(<MD_Fields.ACCOUNT: 0>, 0, 'Account', True)
(<MD_Fields.M_DESCRIPT: 4>, 4, 'Description', False)
If you were only extending Enum
instead of IntEnum
(which is just a shortcut for extending both int
and Enum
), you would use obj = object.__new__(cls)
instead.
The reason that _value_
needs to be set is because EnumMeta.__new__
will use its default behavior for setting _value_
when it wasn't already stored on the member. The relevant portion from the source for EnumMeta.__new__
(in 3.10), which should look familiar from the error that occurred before that attribute was assigned a value in MD_Fields.__new__
:
if not hasattr(enum_member, '_value_'):
if member_type is object:
enum_member._value_ = value
else:
enum_member._value_ = member_type(*args)
Normally, the ._value_
attribute would be populated using the value to the right of the =
when the member was defined. That value ends up being exposed through the .value
attribute for each member.
In 3.11, the error was improved to include the hint that _value_
needs to be set:
TypeError: _value_ not set in __new__, unable to create it
Upvotes: 3