2010-09-02 17 views
5

Si bien soy consciente de que no puede hacer referencia self directamente en un decorador, me preguntaba si es una mala práctica solucionarlo tirando de args[0]. Mi corazonada es que así es, pero quiero estar seguro.¿Es una mala práctica usar uno mismo en decoradores?

Para ser más específicos, estoy trabajando en una API para un servicio web. Aproximadamente la mitad de los comandos requieren que se pase un token que luego se puede usar para deshacerlo. Lo que me gustaría es convertir ese token en un parámetro opcional y si no se proporciona ninguno, generar uno. Generar un token requiere realizar una llamada autenticada al servidor, que necesita datos del objeto.

Aunque sé que podría hacerlo:

def some_command(self, ..., undo_token = None): 
    if undo_token = None: 
     undo_token = self.get_undo_token() 
    ... 
    return fnord 

Siento que podría haber una forma mejor que tener el mismo código en una docena de métodos. Mi idea era escribir un decorador:

@decorator 
def undoable(fn, *args, **kwargs): 
    if 'undo_token' not in kwargs: 
     kwargs['undo_token'] = args[0].get_undo_token() 
    return (fn(*args, **kwargs), kwargs['undo_token']) 

Así que se puede escribir de forma más limpia

@undoable 
def some_command(self, ...): 
    ... 
    return foo 

@undoable 
def some_other_command(self, ...): 
    ... 
    return bar 

Estoy dando a mí mismo para problemas en el momento?

+0

+1 para usar el patrón de decorador para implementar "deshacer". –

+2

No hay nada de malo en que un decorador tenga requisitos sobre los parámetros de la función que se está decorando; solo asegúrate de que esté documentado. –

Respuesta

6

No entiendo lo que estás codifica para undoable - que no es la forma en que normalmente se codifican decoradores y no sé donde que @decorator proviene de (¿hay una from youforgottotelluswhence import decorator o algo aún más el mal ver por qué? No puedo soportar el uso de from para construir "nombres desnudos artificiales" en lugar de usar nombres decorados? -).

Con la codificación decorador normal, por ejemplo ...:

import functools 

def undoable(f): 
    @functools.wraps(f) 
    def wrapper(self, *a, **k): 
     tok = k.get('undo_token') 
     if tok is None: 
      tok = k['undo_token'] = self.get_undo_token() 
     return f(self, *a, **k), tok 
    return wrapper 

no hay absolutamente ningún problema nombrando primero, obligatoria argumento posicional de la envoltura self, y mucha ganancia de claridad en el uso de este bien que la menos legible args[0] .

+1

Luego búscalo: http://pypi.python.org/pypi/decorator/2.3.2 – wheaties

+0

+1 "No puedo soportar el uso de' from' para construir 'nombres desnudos artificiales' " – Pete

+0

Sí, eso' del Decorador de Decorador de Importación' ** realmente ** hace que el código de Q sea imposible de entender sin un " URL de referencia ", como tales" nombres desnudos artificiales "siempre lo hacen - lástima que Michele eligió usarlo en sus documentos, dando a los usuarios un ejemplo tan horrible. Además, al menos en este caso de uso, el módulo decorador de terceros utiliza argumentos que hacen que el código sea muy difícil de seguir, mientras que la sintaxis clara de Python separa "la función decorada" de "los argumentos en el contenedor" - I ' preferiría perder la magia negra para la "preservación de la firma" que perder la claridad del código mushing 'em up. –

2

Los decoradores amplían la funcionalidad de la función que decora de forma genérica. Si los decoradores no hacen ninguna suposición acerca de la función o sus argumentos o kwargs, es en su forma más genérica y puede usarse fácilmente con muchas funciones.

Sin embargo, si desea hacer algo con lo que se está transmitiendo a la función, debería estar bien, pero su aplicabilidad es limitada y puede romperse si cambian los detalles subyacentes que ha utilizado en su decorador.

En el decorador anterior, si el objeto elimina el método get_undo_token(), deberá volver a visitar el decorador también. Está bien hacer eso, pero documentar las restricciones y también agregar esa documentación al método doc.

Hazlo solo si es absolutamente necesario. Sirve para crear decoradores más genéricos.

Cuestiones relacionadas