I'm trying to set up an Enum that will return None if the value is not found. The documentation mentions a function _missing_, but does not explain any of the details regarding the function:
_missing_– a lookup function used when a value is not found; may be overridden
After some looking around, it seems this is a classmethod with the signature cls, value, so I tried to set it up, and its not working.
>>> class G(enum.Enum):
... @classmethod
... def _missing_(cls, value):
... return None
... a = 1
...
>>> G(1)
<G.a: 1>
>>> G(2)
Traceback (most recent call last):
...
ValueError: 2 is not a valid G
>>> G['b']
KeyError: 'b'
>>> G.b
AttributeError: b
A google search suggests that _missing_ only catches the ValueError in the call case, so the KeyError and TypeError do not surprise me, but I do not know why G(2) is raising ValueError instead of returning None.
The 2 main things that the documentation is missing regarding the _missing_ function is the signature in the question, and the fact that the return type MUST be a member of the Enum. If None is returned, then the error simply isn't silenced.
This behaviour is only seen through source inspection or a different error message:
>>> class G(enum.Enum):
... @classmethod
... def _missing_(cls, value):
... return "a truthy value" # I suspected that the error may have been caused by a falsey return
... a = 1
...
>>> G(2)
ValueError: 2 is not a valid G
During handling of the above exception, another exception occured:
Traceback (most recent call last):
...
TypeError: error in G._missing_: returned 'a truthy value' instead of None or a valid member
So the only way to handle this case is to have a sentinal G.none, G.null, G.missing or whatever value is most suitable.
The _missing_() function must return an instance of the enum class it is defined in. It can't return None, a string, or anything else. However, the instance returned by _missing_() does NOT need to be a predefined member of the enum! Instead, you can create a dynamically generated custom enum member on the fly:
from enum import IntEnum
class MyEnum(IntEnum):
A = 1
B = 2
@classmethod
def _missing_(cls, value):
# reject anything other than an int
if not isinstance(value, int):
raise ValueError(f"I don't like {str(type(value))} values: {value!r}")
# Dynamically create a pseudo-member for the undefined value
pseudo_member = int.__new__(cls, value)
pseudo_member._name_ = f"OTHER_{value}"
pseudo_member._value_ = value
return pseudo_member
print(repr(MyEnum(1))) # <MyEnum.A: 1>
print(repr(MyEnum(2))) # <MyEnum.B: 2>
print(repr(MyEnum(3))) # <MyEnum.OTHER_3: 3>
# This line will raise an error because "Hello" is not an int
print(repr(MyEnum("Hello")))
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