覆盖类的 __contains__ 方法 [英] Overriding __contains__ method for a class

查看:61
本文介绍了覆盖类的 __contains__ 方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

类垃圾邮件(枚举):k = 3鸡蛋 = 0火腿 = 1烤豆 = 2

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

if (x in Foo):打印(看起来合法")

因此,我尝试创建一个Enum"基类,在其中覆盖 __contains__ 方法,如下所示:

 类枚举:"""模拟一个枚举."""k = 0 # 在子类中用常量数覆盖@类方法def __contains__(cls, x):"""测试有效的枚举常量 x:枚举中的 x"""返回(范围内的x(cls.k))

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

TypeError: 'type' 类型的参数不可迭代

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

解决方案

为什么会这样?

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

如果您实例化一个对象,然后在创建对象后,将一个 __contains__ 函数添加到实例变量中,也会发生同样的情况.不会调用该函数:

<预><代码>>>>空类:通过...>>>x = 空()>>>x.__contains__ = lambda: 真>>>1 英寸 x回溯(最近一次调用最后一次):文件<stdin>",第 1 行,在 <module> 中类型错误:空"类型的参数不可迭代

<块引用>

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

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

试试这个:

class MetaEnum(type):def __contains__(cls, x):返回范围内的 x(cls.k)

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

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

class BaseEnum(metaclass=MetaEnum):经过类 MyEnum(BaseEnum):k = 3打印(MyEnum 中的 1)# 真

I need to simulate enums in Python, and did it by writing classes like:

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

Now I'd like to test if some constant is a valid choice for a particular Enum-derived class, with the following syntax:

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

Therefore I tried to create an "Enum" base class where I override the __contains__ method like this:

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))

However, when using the in keyword on the class (like the example above), I get the error:

TypeError: argument of type 'type' is not iterable

Why that? Can I somehow get the syntactic sugar I want?

解决方案

Why that?

When you use special syntax like a in Foo, the __contains__ method is looked up on the type of Foo. However, your __contains__ implementation exists on Foo itself, not its type. Foo's type is type, which doesn't implement this (or iteration), thus the error.

The same situation occurs if you instantiate an object and then, after it is created, add a __contains__ function to the instance variables. That function won't be called:

>>> 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

Can I somehow get the syntactic sugar I want?

Yes. As mentioned above, the method is looked up on Foo's type. The type of a class is called a metaclass, so you need a new metaclass that implements __contains__.

Try this one:

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

As you can see, the methods on a metaclass take the metaclass instance -- the class -- as their first argument. This should make sense. It's very similar to a classmethod, except that the method lives on the metaclass and not the class.

Inheritance from a class with a custom metaclass also inherits the metaclass, so you can create a base class like so:

class BaseEnum(metaclass=MetaEnum):
    pass

class MyEnum(BaseEnum):
    k = 3

print(1 in MyEnum) # True

这篇关于覆盖类的 __contains__ 方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆