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__)
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
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