qué patron parametros ejemplo decoradores decorador con python decorator

patron - ¿Por qué decoradores Python en lugar de cierres?



patron decorator python (4)

Todavía no me he vuelto loca por los decoradores en Python.

Ya comencé a usar muchos cierres para hacer cosas como personalizar funciones y clases en mi codificación.

P.ej.

class Node : def __init__(self,val,children) : self.val = val self.children = children def makeRunner(f) : def run(node) : f(node) for x in node.children : run(x) return run tree=Node(1,[Node(2,[]),Node(3,[Node(4,[]),Node(5,[])])]) def pp(n) : print "%s," % n.val printTree = makeRunner(pp) printTree(tree)

Por lo que puedo ver, los decoradores son simplemente una sintaxis diferente para hacer algo similar.

En lugar de

def pp(n) : print "%s," % n.val printTree = makeRunner(pp)

Yo escribiría :

@makeRunner def printTree(n) : print "%s," % n.val

¿Esto es todo lo que hay para los decoradores? ¿O hay una diferencia fundamental que me he perdido?


¿Son tus ejemplos código real, o solo ejemplos?

Si son códigos reales, creo que usas demasiado decoradores, probablemente por tu historial (es decir, que estás acostumbrado a otros lenguajes de programación)

Etapa 1: evitar decoradores

def run(rootnode, func): def _run(node): # recursive internal function func(node) for x in node.children: _run(x) # recurse _run(rootnode) # initial run

Este método de ejecución obsoleta makeRunner. Tu ejemplo se convierte en:

def pp(n): print "%s," % n.val run(tree, pp)

Sin embargo, esto ignora completamente los generadores, entonces ...

Etapa 2: usar generadores

class Node : def __init__(self,val,children) : self.val = val self.children = children def __iter__(self): # recursive yield self for child in self.children: for item in child: # recurse yield item def run(rootnode, func): for node in rootnode: func(node)

Tu ejemplo permanece

def pp(n): print "%s," % n.val run(tree, pp)

Tenga en cuenta que el método especial __iter__ nos permite usar el for node in rootnode: construct. Si no te gusta, simplemente cambia el nombre del método __iter__ a, por ejemplo, walker , y cambia el ciclo de run en: for node in rootnode.walker():
Obviamente, la función de run podría ser un método de class Node de class Node lugar.

Como ve, le sugiero que use directamente run(tree, func) lugar de vincularlos al nombre printTree , pero puede usarlos en un decorador, o puede hacer uso de la función functools.partial :

printTree= functools.partial(run, func=pp)

y de ahí en más, simplemente

printTree(tree)


Si bien es cierto que sintácticamente, los decoradores son simplemente "azúcar", esa no es la mejor manera de pensar sobre ellos.

Los decoradores te permiten tejer funcionalidad en tu código existente sin modificarlo realmente. Y te permiten hacerlo de una manera declarativa.

Esto le permite usar decoradores para hacer una programación orientada a aspectos (AOP). Así que desea utilizar un decorador cuando tenga una preocupación transversal que quiera encapsular en un solo lugar.

El ejemplo por excelencia sería el registro, donde desea registrar la entrada o salida de una función, o ambas cosas. Usar un decorador es equivalente a aplicar consejos (¡log esto!) A un punto de unión (durante la entrada o salida del método).

La decoración de métodos es un concepto como OOP o listas de comprensión. Como usted señala, no siempre es apropiado, y puede ser usado en exceso. Pero en el lugar correcto, puede ser útil para hacer que el código sea más modular y desacoplado.


Siguiendo la referencia de AOP de Dutch Master, descubrirá que el uso de decoradores se vuelve especialmente útil cuando comienza a agregar parámetros para modificar el comportamiento de la función / método decorado, y leer la definición de función anterior es mucho más fácil.

En un proyecto, recuerdo, necesitábamos supervisar toneladas de tareas de apio, así que se nos ocurrió la idea de utilizar un decorador para enchufar y ajustar según fuera necesario, que era algo así como:

class tracked_with(object): """ Method decorator used to track the results of celery tasks. """ def __init__(self, model, unique=False, id_attr=''results_id'', log_error=False, raise_error=False): self.model = model self.unique = unique self.id_attr = id_attr self.log_error = log_error self.raise_error = raise_error def __call__(self, fn): def wrapped(*args, **kwargs): # Unique passed by parameter has priority above the decorator def unique = kwargs.get(''unique'', None) if unique is not None: self.unique = unique if self.unique: caller = args[0] pending = self.model.objects.filter( state=self.model.Running, task_type=caller.__class__.__name__ ) if pending.exists(): raise AssertionError(''Another {} task is already running'' ''''.format(caller.__class__.__name__)) results_id = kwargs.get(self.id_attr) try: result = fn(*args, **kwargs) except Retry: # Retry must always be raised to retry a task raise except Exception as e: # Error, update stats, log/raise/return depending on values if results_id: self.model.update_stats(results_id, error=e) if self.log_error: logger.error(e) if self.raise_error: raise else: return e else: # No error, save results in refresh object and return if results_id: self.model.update_stats(results_id, **result) return result return wrapped

Luego, simplemente decoramos el método de run de las tareas con los parámetros necesarios para cada caso, como por ejemplo:

class SomeTask(Task): @tracked_with(RefreshResults, unique=True, log_error=False) def run(self, *args, **kwargs)...

Luego, cambiar el comportamiento de la tarea (o eliminar el seguimiento por completo) significaba ajustar un parámetro o comentar la línea decorada. Súper fácil de implementar, pero más importante, muy fácil de entender en la inspección.


Los decoradores , en el sentido general, son funciones o clases que envuelven a otro objeto, que extienden o decoran el objeto. El decorador admite la misma interfaz que la función u objeto envuelto, por lo que el receptor ni siquiera sabe que el objeto ha sido decorado.

Un cierre es una función anónima que se refiere a sus parámetros u otras variables fuera de su alcance.

Entonces, básicamente, los decoradores usan cierres y no los reemplazan.

def increment(x): return x + 1 def double_increment(func): def wrapper(x): print ''decorator executed'' r = func(x) # --> func is saved in __closure__ y = r * 2 return r, y return wrapper @double_increment def increment(x): return x + 1 >>> increment(2) decorator executed (3, 6) >>> increment.__closure__ (<cell at 0x02C7DC50: function object at 0x02C85DB0>,) >>> increment.__closure__[0].cell_contents <function increment at 0x02C85DB0>

Entonces el decorador guarda la función original con cierre .

¿Cuál es la diferencia entre cierres y decoradores en Python?