2012-05-04 9 views
7

クラスのための方法、およびは次のようなクラスを書くことでそれをやりました以下の構文を有する由来クラス:オーバーライド__contains__の私はPythonで列挙型をシミュレートする必要が

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

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

。したがって、私はこのような__contains__メソッドをオーバーライドする場合、「列挙」ベースクラスを作成しようとしました

(上記の例のように)クラスにinキーワードを使用している場合しかし、私はエラーを取得する:

TypeError: argument of type 'type' is not iterable 

なぜ?どうにか私が望む統語砂糖を手に入れられますか?

+0

私はコードがために正常に動作することをclarifiyingコメントだと思います: 場合x in Foo(): print( '正当なものと思われます) 質問と回答の両方を理解しやすくします。 タイプ(Foo())はFooですが、タイプ(Foo)はメタクラスです。 –

答えて

13

Why that?

あなたは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 

Can I somehow get the syntactic sugar I want?

はい。前述のように、この方法はFooのタイプで検索されます。クラスのタイプはmetaclassと呼ばれるので、__contains__を実装する新しいメタクラスが必要です。

は試してみてください、この1:

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

ああ、私はメタクラスが存在するという曖昧な考えがありましたが、自分のユースケースは決してありませんでした。これはうまくいく、ありがとう。 – clstaudt

関連する問題