¿Cómo funciona la función que se llama dentro de la statement de clase?

Tiene este codigo

>>> class Foo: ... zope.interface.implements(IFoo) ... ... def __init__(self, x=None): ... self.x = x ... ... def bar(self, q, r=None): ... return q, r, self.x ... ... def __repr__(self): ... return "Foo(%s)" % self.x 

Obviamente, la llamada de zope.interface.implements de alguna manera altera las propiedades y el comportamiento de la clase Foo .

¿Como sucedió esto? ¿Cómo uso este enfoque en mi código?

Código de ejemplo es la parte del módulo zope.interface .

    El detallado “que pasa”

    La función zope.interface.implements() inspecciona la stack de cuadros y altera el espacio de nombres locals() (un dict python) de la clase en construcción. Todo dentro de una statement de class en Python se ejecuta en ese espacio de nombres, y el resultado forma el cuerpo de la clase.

    La función agrega un valor adicional al espacio de nombres de clase, __implements_advice_data__ con algunos datos (las interfaces que ha pasado a la función y la clase __implements_advice_data__ , algo que se usará más adelante).

    Luego agrega o encadena una metaclase para la clase en cuestión, agregando (o modificando una clave __metaclass__ preexistente) en el espacio de nombres. Esto garantiza que en el futuro, cada vez que cree una instancia de la clase, se llamará primero a la metaclase que ahora está instalada.

    De hecho, esta metaclase (el asesor de la clase) es un poco retorcida; se elimina de nuevo después de la primera vez que creas una instancia. Simplemente llama a la callback especificada en __implements_advice_data__ junto con las interfaces que __implements_advice_data__ a la función original implements() , justo después de que elimina la clave __metaclass__ de la clase, o la reemplaza por la original __metaclass__ (que llamó para crear la primera instancia de clase). La callback se limpia después de sí misma, elimina el atributo __implements_advice_data__ de la clase.

    La versión corta

    En resumen, todo el trabajo que hace zope.interface.implements() es:

    • Agregue las interfaces pasadas, junto con una callback a un atributo especial en la clase ( __implements_advice_data__ ).
    • Asegura que la callback se llame la primera vez que cree una instancia, utilizando una metaclase especial.

    Al final, es el equivalente moral de definir sus interfaces como esta:

     class Foo: def __init__(self, x=None): self.x = x def bar(self, q, r=None): return q, r, self.x def __repr__(self): return "Foo(%s)" % self.x zope.interface.classImplements(Foo, IFoo) 

    excepto que la última llamada se pospone hasta que primero creas una instancia de Foo .

    Pero ¿por qué ir a tales longitudes?

    Cuando se desarrolló zope.interface por primera vez, Python aún no tenía decoradores de clase.

    zope.interface.classImplements() debe llamarse por separado, como una función, después de que se haya creado la clase, y una llamada zope.interface.implements() dentro del cuerpo de la clase proporciona mejor documentación sobre qué interfaces implementa una clase. Puede colocarlo en la parte superior de una statement de clase, y todos pueden ver esta información importante cuando miran la clase. Tener una llamada classImplements() ubicada después de la statement de la clase no es tan visible y clara, y para las definiciones largas de la clase, fácilmente se la perderá por completo.

    PEP 3129 finalmente agregó decoradores de clase al lenguaje, y se agregaron a Python 2.6 y 3.0; zope.interface se desarrolló por primera vez en los días de python 2.3 (IIRC).

    Ahora que tenemos decoradores de clase, zope.interface.implements() ha quedado en desuso, y en su lugar puede usar el decorador de clase zope.interface.implementer :

     @zope.interface.implementer(IFoo) class Foo: def __init__(self, x=None): self.x = x def bar(self, q, r=None): return q, r, self.x def __repr__(self): return "Foo(%s)" % self.x 

    Lee la fuente, luke :

    http://svn.zope.org/zope.interface/trunk/src/zope/interface/declarations.py?rev=124816&view=markup

     def _implements(name, interfaces, classImplements): frame = sys._getframe(2) locals = frame.f_locals # Try to make sure we were called from a class def. In 2.2.0 we can't # check for __module__ since it doesn't seem to be added to the locals # until later on. if (locals is frame.f_globals) or ( ('__module__' not in locals) and sys.version_info[:3] > (2, 2, 0)): raise TypeError(name+" can be used only from a class definition.") if '__implements_advice_data__' in locals: raise TypeError(name+" can be used only once in a class definition.") locals['__implements_advice_data__'] = interfaces, classImplements addClassAdvisor(_implements_advice, depth=3)