2012-05-04 50 views
7

muszę symulować stałe teksty w Pythonie, i zrobił to pisząc zajęcia jak:Zastępowanie metoda __contains__ dla klasy

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

Teraz chciałbym sprawdzić, czy jakiś stały jest prawidłowym wyborem dla konkretnego Enum pochodzące z A klasy, z następującą składnią:

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

Dlatego starałem się stworzyć „Enum” klasę bazową gdzie zastąpić metodę __contains__ takiego:

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

Jednak, gdy za pomocą słowa kluczowego in od klasy (jak w przykładzie powyżej), pojawia się błąd:

TypeError: argument of type 'type' is not iterable 

Dlaczego to? Czy mogę jakoś uzyskać cukier syntaktyczny?

+0

myślę, że komentarz clarifiying że kod będzie działać dobrze dla: jeżeli x in Foo(): print ("wydaje się uzasadniony") Ułatwi zrozumienie zarówno pytania, jak i odpowiedzi. Ponieważ typ (Foo()) jest Foo, ale typ (Foo) jest klasą meta. –

Odpowiedz

13

Why that?

Kiedy użyć specjalnej składni jak a in Foo metoda __contains__ jest wzrok od rodzaju Foo. Jednak twoja implementacja __contains__ istnieje tylko na Foo, a nie na jej typ. Typ Foo to type, który nie implementuje tej (lub iteracji), a tym samym błędu.

Taka sama sytuacja występuje w przypadku wystąpienia obiektu, a następnie po jest tworzony, dodać __contains__ funkcję zmiennych instancji. Że funkcja nie zostanie wywołana:

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

Tak. Jak wspomniano powyżej, metoda jest sprawdzana na typie Foo. Typ klasy nazywa się metaclass, więc potrzebujesz nowej metaklasy, która implementuje __contains__.

Spróbuj tego:

class MetaEnum(type): 
    def __contains__(cls, x): 
      return x in range(cls.k) 

Jak widać, metody na metaklasą wziąć metaklasą instancji - klasę - jako pierwszy argument. To powinno mieć sens. Jest bardzo podobny do metody klasowej, z tym wyjątkiem, że metoda żyje w metaklassie, a nie w klasie.

Dziedziczenie z klasy z niestandardowym metaklasą dziedziczy również metaklasą, więc można utworzyć klasę bazową tak:

class BaseEnum(metaclass=MetaEnum): 
    pass 

class MyEnum(BaseEnum): 
    k = 3 

print(1 in MyEnum) # True 
+1

Ach, miałem mgliste pojęcie, że metaclasses istnieją, ale nigdy nie miałem własnego przypadku użycia. To działa dobrze, dzięki. – clstaudt

Powiązane problemy