覆盖类的__contains__方法

cls*_*udt 11 python python-3.x

我需要在Python中模拟枚举,并通过编写类来完成它:

class Spam(Enum):
    k = 3
    EGGS = 0
    HAM = 1
    BAKEDBEANS = 2
Run Code Online (Sandbox Code Playgroud)

现在,我想测试某个常量是否是特定Enum派生类的有效选择,使用以下语法:

if (x in Foo):
    print("seems legit")
Run Code Online (Sandbox Code Playgroud)

因此,我尝试创建一个"Enum"基类,我在其中覆盖这样的__contains__方法:

class Enum:
    """
    Simulates an enum.
    """

    k = 0 # overwrite in subclass with number of constants

    @classmethod
    def __contains__(cls, x):
        """
        Test for valid enum constant x:
            x in Enum
        """
        return (x in range(cls.k))
Run Code Online (Sandbox Code Playgroud)

但是,在in类上使用关键字时(如上例所示),我收到错误:

TypeError: argument of type 'type' is not iterable
Run Code Online (Sandbox Code Playgroud)

为什么?我可以以某种方式获得我想要的语法糖吗?

Dev*_*rre 18

为什么?

当您使用特殊语法时a in Foo,会查询该__contains__方法的类型Foo.但是,您的__contains__实现Foo本身存在,而不是其类型.Foo的类型是type,它没有实现这个(或迭代),因此错误.

如果实例化一个对象,然后创建对象,__contains__向实例变量添加一个函数,则会出现相同的情况.该函数不会被调用:

>>> class Empty: pass
... 
>>> x = Empty()
>>> x.__contains__ = lambda: True
>>> 1 in x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: argument of type 'Empty' is not iterable
Run Code Online (Sandbox Code Playgroud)

我可以以某种方式获得我想要的语法糖吗?

是.如上所述,该方法是在Foo类型上查找的.类的类型称为元类,因此您需要一个实现的新元类__contains__.

试试这个:

class MetaEnum(type):
    def __contains__(cls, x):
            return x in range(cls.k)
Run Code Online (Sandbox Code Playgroud)

如您所见,元类上的方法将元类实例(类)作为它们的第一个参数.这应该是有道理的.它与类方法非常相似,只不过该方法存在于元类而不是类.

从具有自定义元类的类继承也会继承元类,因此您可以创建一个基类,如下所示:

class BaseEnum(metaclass=MetaEnum):
    pass

class MyEnum(BaseEnum):
    k = 3

print(1 in MyEnum) # True
Run Code Online (Sandbox Code Playgroud)

  • 啊,我有一个模糊的想法,元类存在,但我从来没有自己的用例。这很好用,谢谢。 (2认同)