__contains__ and Python3.8 enum.Enum
Question:
SETUP: Python 3.8.2; Some Enum Class with an overloaded __contains__()
function.
from enum import Enum
class Something(Enum):
A = 1
def __contains__(self, Other):
return Other in self.__class__.__members__.keys()
TEST 1: Using values from the enum itself.
print("A:", Something.A in Something)
works fine (here result = A: True
).
TEST 2: Using non-enum values.
print("1:", 1 in Something)
fails with an exception, namely
TypeError: unsupported operand type(s) for 'in': 'int' and 'EnumMeta'
QUESTION:
How is it possible to achieve an in
functionality (membership operator) where the left operand can be anything? That is, it should be possible to write something like
if anything in Something:
...
without having to check the type of anything
.
Answers:
Defining Something.__contains__
lets you write something like 1 in Something.A
. For what you want, you would need to subclass EnumMeta
and use the result in defining Something
.
In some sense, Enum.__new__
already does the check we want; passing a value to the type either returns the appropriate instance, or raises a ValueError
.
>>> Something.A
<Something.A: 1>
>>> Something(Something.A)
<Something.A: 1>
>>> Something(1)
<Something.A: 1>
>>> Something(3)
ValueError: 3 is not a valid Something
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
[...]
So, our new metaclass’s __contains__
method simply has to try to retrieve the given value from the given type, returning True
if it succeeds and False
if an exception is raised.
from enum import EnumMeta, Enum
class MyMeta(EnumMeta):
def __contains__(self, other):
try:
self(other)
except ValueError:
return False
else:
return True
class Something(Enum, metaclass=MyMeta):
A = 1
assert Something.A in Something
assert 1 in Something
assert 2 not in Something
If you want 1 in Something
to return False
instead, just catch the TypeError
raised by super().__contains__
.
class MyMeta(EnumMeta):
def __contains__(self, other):
try:
return super().__contains__(other)
except TypeError:
return False
SETUP: Python 3.8.2; Some Enum Class with an overloaded __contains__()
function.
from enum import Enum
class Something(Enum):
A = 1
def __contains__(self, Other):
return Other in self.__class__.__members__.keys()
TEST 1: Using values from the enum itself.
print("A:", Something.A in Something)
works fine (here result = A: True
).
TEST 2: Using non-enum values.
print("1:", 1 in Something)
fails with an exception, namely
TypeError: unsupported operand type(s) for 'in': 'int' and 'EnumMeta'
QUESTION:
How is it possible to achieve an in
functionality (membership operator) where the left operand can be anything? That is, it should be possible to write something like
if anything in Something:
...
without having to check the type of anything
.
Defining Something.__contains__
lets you write something like 1 in Something.A
. For what you want, you would need to subclass EnumMeta
and use the result in defining Something
.
In some sense, Enum.__new__
already does the check we want; passing a value to the type either returns the appropriate instance, or raises a ValueError
.
>>> Something.A
<Something.A: 1>
>>> Something(Something.A)
<Something.A: 1>
>>> Something(1)
<Something.A: 1>
>>> Something(3)
ValueError: 3 is not a valid Something
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
[...]
So, our new metaclass’s __contains__
method simply has to try to retrieve the given value from the given type, returning True
if it succeeds and False
if an exception is raised.
from enum import EnumMeta, Enum
class MyMeta(EnumMeta):
def __contains__(self, other):
try:
self(other)
except ValueError:
return False
else:
return True
class Something(Enum, metaclass=MyMeta):
A = 1
assert Something.A in Something
assert 1 in Something
assert 2 not in Something
If you want 1 in Something
to return False
instead, just catch the TypeError
raised by super().__contains__
.
class MyMeta(EnumMeta):
def __contains__(self, other):
try:
return super().__contains__(other)
except TypeError:
return False