14

Python で列挙型をシミュレートする必要があり、次のようなクラスを記述して実行しました。

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

ここで、次の構文を使用して、特定の Enum 派生クラスに対して有効な選択である定数があるかどうかをテストしたいと思います。

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

__contains__したがって、次のようにメソッドをオーバーライドする「Enum」基本クラスを作成しようとしました。

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__実装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の型で検索されます。クラスの型はメタクラスと呼ばれるため、 を実装する新しいメタクラスが必要です__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 に答える