覆盖类的 __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__的解决方法