__contains__ 和 Python3.8 enum.Enum

Fra*_*fer 4 python enums

设置:Python 3.8.2;一些带有重载__contains__()函数的枚举类。

  from enum import Enum
  
  class Something(Enum):
      A = 1
      def __contains__(self, Other):
          return Other in self.__class__.__members__.keys()
Run Code Online (Sandbox Code Playgroud)

测试 1:使用枚举本身的值。

  print("A:", Something.A in Something)
Run Code Online (Sandbox Code Playgroud)

工作正常(这里结果= A: True)。

测试 2:使用非枚举值。

  print("1:", 1 in Something)
Run Code Online (Sandbox Code Playgroud)

失败但有异常,即

  TypeError: unsupported operand type(s) for 'in': 'int' and 'EnumMeta'
Run Code Online (Sandbox Code Playgroud)

问题

如何实现in左操作数可以是任何内容的功能(成员运算符)?也就是说,应该可以写类似的东西

if anything in Something:
    ...
Run Code Online (Sandbox Code Playgroud)

无需检查 的类型anything

che*_*ner 9

定义Something.__contains__可以让你写出类似的东西1 in Something.A。对于您想要的,您需要子类化EnumMeta并在定义中使用结果Something

从某种意义上说,我们想要的检查Enum.__new__ 已经完成了;将值传递给类型要么返回适当的实例,要么引发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):
[...]
Run Code Online (Sandbox Code Playgroud)

因此,我们新的元类的__contains__方法只需尝试从给定类型检索给定值,True如果成功并且False引发异常则返回。

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
Run Code Online (Sandbox Code Playgroud)

如果你想1 in Something返回False,只需抓住TypeError提出的super().__contains__

class MyMeta(EnumMeta):
    def __contains__(self, other):
        try:
            return super().__contains__(other)
        except TypeError:
            return False
Run Code Online (Sandbox Code Playgroud)