Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

__contains__ and Python3.8 enum.Enum

Tags:

python

enums

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.

like image 478
Frank-Rene Schäfer Avatar asked Oct 29 '25 09:10

Frank-Rene Schäfer


1 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
like image 55
chepner Avatar answered Oct 31 '25 23:10

chepner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!