2012-09-16 12 views
14

Supongamos que la clase siguiente:¿por qué __getitem__ no puede ser classmethod?

class Class(object): 
    @classmethod 
    def getitem(*args): 
     print 'getitem %s' % (args,) 
    @classmethod 
    def __getitem__(*args): 
     print '__getitem__ %s' % (args,) 

El método GetItem se comporta como se esperaba: recibe Class como primer argumento, pero __getitem__ recibe type como primer argumento:

calling Class.getitem(test) 
getitem (<class '__main__.Class'>, 'test') 

calling obj.getitem(test) 
getitem (<class '__main__.Class'>, 'test') 

calling Class[test] 
'type' object has no attribute '__getitem__' 

calling obj[test] 
__getitem__ (<class '__main__.Class'>, 'test') 

¿Cuál es la magia que hay detrás de __getitem__?

Respuesta

18

métodos especiales se buscan en la clase, y no en la instancia - a diferencia de los métodos habituales de ordenador que son en la instancia primero. Consulte Special method lookup en los documentos del modelo de datos de Python.

Pensando en Class como una instancia de type, esto significa que cuando usted hace

Class.getitem(test) 

Se ve por primera vez para exactamente lo que le dices: un método en el Class 's propios atributos llama getitem. Sin embargo, cuando se utiliza

Class[test] 

se salta esto, y va directamente a type (siendo la clase de Class, o su metaclase), y así llama type.__getitem__(Class, test). Entonces, lo que está sucediendo no es que __getitem__ obtenga type como primer argumento (aún obtendría Class, como lo hace si explícitamente Class.__getitem__(test)), es que el __getitem__ que Python busca en este caso no existe. Para que exista, debe definir su propia metaclase para Class que lo define como un método de instancia, en lugar de definirlo en Class como un método de clase.

+0

'Pensando en Class como una instancia de tipo'. Gracias. Gracias. Gracias. – norbertpy

9

Cuando llama al x[test], el intérprete inspecciona type(x) para el atributo __getitem__. En el caso de Class[test] es la metaclase Class, es decir, type. Si quiere tener un __getitem__ para toda la clase, defínalo dentro de una nueva metaclase. (Ni que decir tiene, que es una especie de magia, como cualquier cosa que se hace con metaclases)

class Meta(type): 
    def __getitem__(self, arg): 
     print "__getitem__:", arg 


class X(object): 
    __metaclass__ = Meta 

X['hello'] # output: __getitem__ hello 
+0

Esa es la solución para la pregunta planteada, pero es demasiada codificación para dicho azúcar sintáctico. Y en el código real, estoy tratando de __getitem__ sobre la metaclase misma :) – qMax

Cuestiones relacionadas