我需要在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
为什么?我能以某种方式获得我想要的语法糖吗?
答案 0 :(得分:15)
为什么?
当您使用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