2012-05-04 12 views
7

necesito para simular las enumeraciones en Python, y lo hizo escribiendo clases como:método __contains__ primordial de una clase

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

Ahora me gustaría probar si alguna constante es una opción válida para una enumeración particular, clase derivada de, con la siguiente sintaxis:

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

Por lo tanto tratado de crear una clase base "Enum" donde anular el método __contains__ como esto:

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

Sin embargo, cuando se utiliza la palabra clave in en la clase (como el ejemplo anterior), me sale el error:

TypeError: argument of type 'type' is not iterable 

¿Por que? ¿De alguna manera puedo obtener el azúcar sintáctico que quiero?

+0

Creo que un comentario clarifiying que el código que funciona bien para: si x en Foo(): imprimir ('parece legítimo') Haría que tanto la pregunta como la respuesta fueran más fáciles de entender. Porque el tipo (Foo()) es Foo pero el tipo (Foo) es la metaclase. –

Respuesta

13

Why that?

Cuando se utiliza una sintaxis especial como a in Foo, el método __contains__ se busca el tipo de Foo. Sin embargo, su implementación __contains__ existe en el Foo, no es su tipo. El tipo Foo es type, que no implementa esto (o iteración), por lo tanto, el error.

Ocurre lo mismo si crea una instancia de un objeto y luego, después de crear, agrega una función __contains__ a las variables de instancia. Esa función no se llamará:

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

Sí. Como se mencionó anteriormente, el método se buscó en el tipo Foo. El tipo de una clase se llama metaclass, por lo que necesita una nueva metaclase que implemente __contains__.

prueba este:

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

Como se puede ver, los métodos en una metaclase toman la instancia metaclase - la clase - como primer argumento. Esto debería tener sentido. Es muy similar a un método de clase, excepto que el método vive en la metaclase y no en la clase.

herencia de una clase con una metaclase a medida también hereda la metaclase, para que pueda crear una clase base, así:

class BaseEnum(metaclass=MetaEnum): 
    pass 

class MyEnum(BaseEnum): 
    k = 3 

print(1 in MyEnum) # True 
+1

Ah, tenía una vaga idea de que las metaclases existen, pero nunca tuve un caso de uso propio. Esto funciona bien, gracias. – clstaudt

Cuestiones relacionadas