En Python 2.5, ¿hay alguna forma de crear un decorador que decore una clase? Específicamente, quiero usar un decorador para agregar un miembro a una clase y cambiar el constructor para tomar un valor para ese miembro.
Buscando algo como lo siguiente (que tiene un error de sintaxis en 'class Foo:':
def getId(self): return self.__id
class addID(original_class):
def __init__(self, id, *args, **kws):
self.__id = id
self.getId = getId
original_class.__init__(self, *args, **kws)
@addID
class Foo:
def __init__(self, value1):
self.value1 = value1
if __name__ == '__main__':
foo1 = Foo(5,1)
print foo1.value1, foo1.getId()
foo2 = Foo(15,2)
print foo2.value1, foo2.getId()
Supongo que lo que realmente busco es una forma de hacer algo como una interfaz C # en Python. Necesito cambiar mi paradigma, supongo.
fuente
Además de la pregunta de si los decoradores de clase son la solución correcta para su problema:
En Python 2.6 y superior, hay decoradores de clase con la sintaxis @, por lo que puede escribir:
En versiones anteriores, puede hacerlo de otra manera:
Sin embargo, tenga en cuenta que esto funciona igual que para los decoradores de funciones, y que el decorador debe devolver la clase nueva (o original modificada), que no es lo que está haciendo en el ejemplo. El decorador addID se vería así:
A continuación, puede usar la sintaxis adecuada para su versión de Python como se describe anteriormente.
Pero estoy de acuerdo con otros en que la herencia es más adecuada si desea anular
__init__
.fuente
Nadie ha explicado que puede definir dinámicamente clases. Entonces puede tener un decorador que defina (y devuelva) una subclase:
Que se puede usar en Python 2 (el comentario de Blckknght que explica por qué debería continuar haciendo esto en 2.6+) de esta manera:
Y en Python 3 como este (pero tenga cuidado de usar
super()
en sus clases):Para que pueda tener su pastel y comerlo, ¡herencia y decoradores!
fuente
super
llamadas en la clase original. SiFoo
tuviera un métodofoo
llamadosuper(Foo, self).foo()
así, se repetiría infinitamente, porque el nombreFoo
está vinculado a la subclase devuelta por el decorador, no a la clase original (a la que ningún nombre puede acceder). Python 3 sin argumentossuper()
evita este problema (supongo que a través de la misma magia del compilador que le permite funcionar). También puede solucionar el problema decorando manualmente la clase con un nombre diferente (como lo hizo en el ejemplo de Python 2.5).Esa no es una buena práctica y no existe un mecanismo para hacerlo debido a eso. La forma correcta de lograr lo que quieres es la herencia.
Echa un vistazo a la documentación de la clase .
Un pequeño ejemplo:
De esta manera, Boss tiene todo lo que
Employee
tiene, también con su propio__init__
método y sus propios miembros.fuente
Estoy de acuerdo en que la herencia se ajusta mejor al problema planteado.
Encontré esta pregunta realmente útil en las clases de decoración, gracias a todos.
Aquí hay otro par de ejemplos, basados en otras respuestas, que incluyen cómo la herencia afecta las cosas en Python 2.7 (y @wraps , que mantiene la cadena de documentación de la función original, etc.):
A menudo desea agregar parámetros a su decorador:
Salidas:
He usado un decorador de funciones, ya que me parece más conciso. Aquí hay una clase para decorar una clase:
Una versión más robusta que comprueba esos paréntesis y funciona si los métodos no existen en la clase decorada:
Los
assert
cheques que el decorador no se ha utilizado sin paréntesis. Si es así, la clase que se está decorando se pasa almsg
parámetro del decorador, que genera unAssertionError
.@decorate_if
solo aplica eldecorator
sicondition
evalúa aTrue
.El
getattr
,callable
prueba, y@decorate_if
se utiliza para que el decorador no se rompe si elfoo()
método no existe en la clase de ser decorado.fuente
En realidad, aquí hay una implementación bastante buena de un decorador de clase:
https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py
De hecho, creo que esta es una implementación bastante interesante. Debido a que subclasifica la clase que decora, se comportará exactamente como esta clase en cosas como los
isinstance
cheques.Tiene un beneficio adicional: no es raro que la
__init__
declaración en un formulario django personalizado realice modificaciones o adiciones, porself.fields
lo que es mejorself.fields
que ocurran cambios después de que todo se__init__
haya ejecutado para la clase en cuestión.Muy inteligente.
Sin embargo, en su clase realmente desea que la decoración altere el constructor, lo que no creo que sea un buen caso de uso para un decorador de clase.
fuente
Aquí hay un ejemplo que responde a la pregunta de devolver los parámetros de una clase. Además, aún respeta la cadena de herencia, es decir, solo se devuelven los parámetros de la clase misma. La función
get_params
se agrega como un ejemplo simple, pero se pueden agregar otras funcionalidades gracias al módulo de inspección.fuente
Django tiene
method_decorator
un decorador que convierte cualquier decorador en un decorador de métodos, puede ver cómo se implementa endjango.utils.decorators
:https://github.com/django/django/blob/50cf183d219face91822c75fa0a15fe2fe3cb32d/django/utils/decorators.py#L53
https://docs.djangoproject.com/en/3.0/topics/class-based-views/intro/#decorating-the-class
fuente