Tengo un problema en Python, por lo cual no puedo encontrar ninguna solución limpia ...Python: métodos metaclase + envueltos + herencia = problemas
Al llamar a algunos métodos, quiero ejecutar algún código antes de la ejecución del método y después. En orden (entre muchas otras cosas) para establecer y limpiar automáticamente una variable context
.
Para lograr esto, se han declarado lo siguiente metaclase:
class MyType(type):
def __new__(cls, name, bases, attrs):
#wraps the 'test' method to automate context management and other stuff
attrs['test'] = cls.other_wrapper(attrs['test'])
attrs['test'] = cls.context_wrapper(attrs['test'])
return super(MyType, cls).__new__(cls, name, bases, attrs)
@classmethod
def context_wrapper(cls, operation):
def _manage_context(self, *args, **kwargs):
#Sets the context to 'blabla' before the execution
self.context = 'blabla'
returned = operation(self, *args, **kwargs)
#Cleans the context after execution
self.context = None
return returned
return _manage_context
@classmethod
def other_wrapper(cls, operation):
def _wrapped(self, *args, **kwargs):
#DO something with self and *args and **kwargs
return operation(self, *args, **kwargs)
return _wrapped
Esto funciona como un encanto:
class Parent(object):
__metaclass__ = MyType
def test(self):
#Here the context is set:
print self.context #prints blabla
Pero tan pronto como quiero subclase Parent
, aparecen problemas, cuando llamo al método principal con super
:
class Child(Parent):
def test(self):
#Here the context is set too
print self.context #prints blabla
super(Child, self).test()
#But now the context is unset, because Parent.test is also wrapped by _manage_context
#so this prints 'None', which is not what was expected
print self.context
He pensado en guardar el contexto antes de establecerlo en un nuevo valor, pero eso solo resuelve parcialmente el problema ...
De hecho, (espere, esto es difícil de explicar), se llama el método principal, el envolturas se ejecutan, pero reciben *args
y **kwargs
dirigidas a Parent.test
, mientras self
es una instancia Child
, por lo self
atributos tienen valores irrelevantes si quiero retarlos con *args
y **kwargs
(por ejemplo, para fines de validación automatizado), ejemplo:
@classmethod
def validation_wrapper(cls, operation):
def _wrapped(self, *args, **kwargs):
#Validate the value of a kwarg
#But if this is executed because we called super(Child, self).test(...
#`self.some_minimum` will be `Child.some_minimum`, which is irrelevant
#considering that we called `Parent.test`
if not kwarg['some_arg'] > self.some_minimum:
raise ValueError('Validation failed')
return operation(self, *args, **kwargs)
return _wrapped
Así que, básicamente, a solv e este problema que veo dos soluciones:
que impiden que los envoltorios para ser ejecutado cuando el método se llama con
super(Child, self)
tener un
self
que siempre es del tipo "correcto"
Ambas soluciones me parecen imposibles ... ¿Alguien tiene una idea sobre cómo resolver esto? Una sugerencia ?
¿Por qué no puedes simplemente usar un decorador para eso? –
¿Está buscando algún tipo de soporte de programación orientado a aspectos en Python? Consulte http://stackoverflow.com/questions/286958/any-aop-support-library-for-python para obtener algunas ideas al respecto. Esto no es exactamente lo que está pidiendo, sino que trata de resolver sus necesidades originales. – Makis
@ Space_C0wb0y: porque quiero poder redeclarar 'test' en una subclase, ¡sin tener que redecorarlo con 5 decoradores! Y de todos modos eso no resolvería mi problema: la forma en que envuelvo los métodos de la metaclase es exactamente equivalente a usar decoradores. – sebpiq