14

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

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

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

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

因此,我尝试创建一个“枚举”基类,并在其中覆盖如下__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

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

4

1 回答 1

22

为什么?

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

如果您实例化一个对象,然后创建它之后,将一个__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's 类型上查找的。类的类型称为元类,因此您需要一个新的元类来实现__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
于 2012-05-04T09:14:21.493 回答