una programacion otra orientada objetos metodos metodo llamar instanciar herencia decoradores como clases clase python decorator python-2.5

programacion - Decorador de clase Python



metodos en python (6)

Además de la cuestión 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 @ -syntax, por lo que puede escribir:

@addID class Foo: pass

En versiones anteriores, puede hacerlo de otra manera:

class Foo: pass Foo = addID(Foo)

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 modificado), que no es lo que está haciendo en el ejemplo. El decorador addID se vería así:

def addID(original_class): orig_init = original_class.__init__ # Make copy of original __init__, so we can call it without recursion def __init__(self, id, *args, **kws): self.__id = id self.getId = getId orig_init(self, *args, **kws) # Call the original __init__ original_class.__init__ = __init__ # Set the class'' __init__ to the new one return original_class

Luego puede usar la sintaxis apropiada para su versión de Python como se describe arriba.

Pero estoy de acuerdo con otros en que la herencia es más adecuada si quieres anular __init__ .

En Python 2.5, ¿hay alguna manera 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 ''clase 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()

Creo que lo que realmente busco es una forma de hacer algo como una interfaz C # en Python. Necesito cambiar mi paradigma, supongo.


De hecho, hay una implementación bastante buena de un decorador de clase aquí:

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 verificaciones de isinstance .

Tiene un beneficio adicional: no es raro que la sentencia __init__ en un Formulario django personalizado haga modificaciones o adiciones a self.fields así que es mejor que los cambios en self.fields sucedan después de que __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.


Esa no es una buena práctica y no hay un mecanismo para hacerlo por eso. La forma correcta de lograr lo que quieres es la herencia.

Eche un vistazo a la documentación de la clase .

Un pequeño ejemplo:

class Employee(object): def __init__(self, age, sex, siblings=0): self.age = age self.sex = sex self.siblings = siblings def born_on(self): today = datetime.date.today() return today - datetime.timedelta(days=self.age*365) class Boss(Employee): def __init__(self, age, sex, siblings=0, bonus=0): self.bonus = bonus Employee.__init__(self, age, sex, siblings)

De esta forma, Boss tiene todo lo que tiene Employee , con su propio método __init__ y sus propios miembros.


Estoy de acuerdo en que la herencia es una mejor opción para el problema planteado.

Sin embargo, encontré esta pregunta realmente útil en la decoración de clases, 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 documentos de la función original, etc.):

def dec(klass): old_foo = klass.foo @wraps(klass.foo) def decorated_foo(self, *args ,**kwargs): print(''@decorator pre %s'' % msg) old_foo(self, *args, **kwargs) print(''@decorator post %s'' % msg) klass.foo = decorated_foo return klass @dec # No parentheses class Foo...

A menudo quieres agregar parámetros a tu decorador:

from functools import wraps def dec(msg=''default''): def decorator(klass): old_foo = klass.foo @wraps(klass.foo) def decorated_foo(self, *args ,**kwargs): print(''@decorator pre %s'' % msg) old_foo(self, *args, **kwargs) print(''@decorator post %s'' % msg) klass.foo = decorated_foo return klass return decorator @dec(''foo decorator'') # You must add parentheses now, even if they''re empty class Foo(object): def foo(self, *args, **kwargs): print(''foo.foo()'') @dec(''subfoo decorator'') class SubFoo(Foo): def foo(self, *args, **kwargs): print(''subfoo.foo() pre'') super(SubFoo, self).foo(*args, **kwargs) print(''subfoo.foo() post'') @dec(''subsubfoo decorator'') class SubSubFoo(SubFoo): def foo(self, *args, **kwargs): print(''subsubfoo.foo() pre'') super(SubSubFoo, self).foo(*args, **kwargs) print(''subsubfoo.foo() post'') SubSubFoo().foo()

Productos:

@decorator pre subsubfoo decorator subsubfoo.foo() pre @decorator pre subfoo decorator subfoo.foo() pre @decorator pre foo decorator foo.foo() @decorator post foo decorator subfoo.foo() post @decorator post subfoo decorator subsubfoo.foo() post @decorator post subsubfoo decorator

He usado un decorador de funciones, ya que las encuentro más concisas. Aquí hay una clase para decorar una clase:

class Dec(object): def __init__(self, msg): self.msg = msg def __call__(self, klass): old_foo = klass.foo msg = self.msg def decorated_foo(self, *args, **kwargs): print(''@decorator pre %s'' % msg) old_foo(self, *args, **kwargs) print(''@decorator post %s'' % msg) klass.foo = decorated_foo return klass

Una versión más robusta que busca esos paréntesis, y funciona si los métodos no existen en la clase decorada:

from inspect import isclass def decorate_if(condition, decorator): return decorator if condition else lambda x: x def dec(msg): # Only use if your decorator''s first parameter is never a class assert not isclass(msg) def decorator(klass): old_foo = getattr(klass, ''foo'', None) @decorate_if(old_foo, wraps(klass.foo)) def decorated_foo(self, *args ,**kwargs): print(''@decorator pre %s'' % msg) if callable(old_foo): old_foo(self, *args, **kwargs) print(''@decorator post %s'' % msg) klass.foo = decorated_foo return klass return decorator

La assert comprueba que el decorador no se haya utilizado sin paréntesis. Si lo tiene, la clase que se está decorando se pasa al parámetro msg del decorador, lo que genera un AssertionError .

@decorate_if solo aplica el decorator si la condition evalúa como True .

El getattr , prueba callable y @decorate_if se utilizan para que el decorador no se rompa si el método foo() no existe en la clase que se está decorando.


Me gustaría secundar la idea de que es posible que desee considerar una subclase en lugar del enfoque que ha descrito. Sin embargo, sin conocer su escenario específico, YMMV :-)

Lo que estás pensando es una metaclase. La función __new__ en una metaclase pasa la definición propuesta completa de la clase, que luego puede reescribir antes de que se cree la clase. En ese momento, puede subcontratar al constructor para obtener uno nuevo.

Ejemplo:

def substitute_init(self, id, *args, **kwargs): pass class FooMeta(type): def __new__(cls, name, bases, attrs): attrs[''__init__''] = substitute_init return super(FooMeta, cls).__new__(cls, name, bases, attrs) class Foo(object): __metaclass__ = FooMeta def __init__(self, value1): pass

Reemplazar el constructor es tal vez un poco dramático, pero el lenguaje proporciona soporte para este tipo de profunda introspección y modificación dinámica.


Nadie ha explicado que se pueden definir clases dinámicamente. Entonces puede tener un decorador que defina (y regrese) una subclase:

def addId(cls): class AddId(cls): def __init__(self, id, *args, **kargs): super(AddId, self).__init__(*args, **kargs) self.__id = id def getId(self): return self.__id return AddId

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:

class Foo: pass FooId = addId(Foo)

Y en Python 3 de esta manera (pero tenga cuidado de usar super() en sus clases):

@addId class Foo: pass

Así que puedes tener tu pastel y comértelo: ¡herencia y decoradores!