覆盖类的 __contains__ 方法

Posted

技术标签:

【中文标题】覆盖类的 __contains__ 方法【英文标题】:Overriding __contains__ method for a class 【发布时间】:2012-05-13 19:41:25 【问题描述】:

我需要在 Python 中模拟枚举,并通过编写如下类来实现:

class Spam(Enum):
    k = 3
    EGGS = 0
    HAM = 1
    BAKEDBEANS = 2

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

if (x in Foo):
    print("seems legit")

因此,我尝试创建一个“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))

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

TypeError: argument of type 'type' is not iterable

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

【问题讨论】:

我认为代码可以正常工作的澄清评论: if x in Foo(): print('seems legit') 将使问题和答案更容易理解。因为 type(Foo()) 是 Foo 而 type(Foo) 是元类。 【参考方案1】:

为什么?

当你使用像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

我能以某种方式得到我想要的语法糖吗?

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

试试这个:

class MetaEnum(type):
    def __contains__(cls, x):
            return x in range(cls.k)

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

从具有自定义元类的类继承也继承了元类,因此您可以像这样创建基类:

class BaseEnum(metaclass=MetaEnum):
    pass

class MyEnum(BaseEnum):
    k = 3

print(1 in MyEnum) # True

【讨论】:

啊,我模糊地认为元类存在,但我从来没有自己的用例。这很好用,谢谢。 8 年后,这个精彩的答案让我在将一些包切换到 Python 3.8 时遇到的问题走上了正轨。我posted here我最终得到了什么,以防其他人在这里遇到类似问题。

以上是关于覆盖类的 __contains__ 方法的主要内容,如果未能解决你的问题,请参考以下文章

Python3基础 super层层调用父类的__init__方法 子类的__init__覆盖了父类的__init__的解决方法

用数据模型方法覆盖 * 和 ** 解包?

javaSE_12_方法覆盖(重写)和多态

覆盖 Django 模型 __init__ 方法

uint64_t t3 = MAXDWORD + 1 == 0?

类的内置方法