2009-10-30 13 views
24

Este es el primer ejemplo que encontramos cuando nos enfrentamos con decoradores. Pero no soy capaz de darme cuenta de lo que exactamente me gustaría.Registro de Python usando un decorador

Un simple decorador llamado LOG. Debe funciona de esta manera:

@LOG 
def f(a, b=2, *c, **d): 
    pass 

Y el resultado debe ser algo como:

f(1, pippo=4, paperino='luca') 
===== Enter f ===== 
a = 1 
b = 2 
pippo = 4 
paperino = luca 
===== Exit f ===== 

Donde cada argumento que se pasa como parámetro a la función se identifica por su valor.

he descubierto que el problema es más difícil de lo que pensaba, principalmente a causa de las muchas maneras diferentes que puede pasar argumentos a una función (pensar tuplas con * C o diccionarios con ** d).

Intenté una solución pero no estoy seguro de que sea correcta. Es somethink así:

def LOG(fn): 
    import inspect 
    varList, _, _, default = inspect.getargspec(fn) 
    d = {} 
    if default is not None: 
     d = dict((varList[-len(default):][i], v) for i, v in enumerate(default)) 
    def f(*argt, **argd): 
     print ('Enter %s' % fn).center(100, '=') 
     d.update(dict((varList[i], v) for i, v in enumerate(argt))) 
     d.update(argd) 
     for c in d.iteritems(): 
      print '%s = %s' % c 
     ret = fn(*argt, **argd) 
     print 'return: %s' % ret 
     print ('Exit %s' % fn).center(100, '=') 
     return ret 
    return f 

creo que no es tan fácil como esperaba, pero es raro que no encontré lo que buscaba en Google.

¿Me puede decir si mi solución está bien? ¿O puede sugerir una mejor solución al problema que propuse?

Gracias a todos.

+2

¿Funciona? Si funciona, entonces está bien. Si no es así, entonces tienes un problema. Háganos una pregunta específica sobre ese problema. –

+0

Sí, tienes razón. En realidad, parece funcionar. Pero mientras escribía hay muchas maneras complejas de pasar argumentos a una función de Python: usando tuplas, diccionarios, argumentos predeterminados, ... En realidad, incluso si parece funcionar, no estoy seguro de si las implementaciones correctas funcionarán. en cada caso. – Luca

+2

Al haber sido víctima de esto, ¿esta función, en la forma absolutamente general que describe, es necesaria para continuar desarrollando su aplicación? Parece que el mecanismo de registro ha llamado su atención, y sé por experiencia que puede ser un terrible asesino de productividad. –

Respuesta

5

Lo único que noté es que la construcción dict((varList[i], v) for i, v in enumerate(argt)) que utilizó dos veces es en realidad dict(zip(varList,argt)).

Aparte de eso, solo tengo metacrítica: ninguna de las anteriores pertenece a un archivo de registro.

En vez de ir a los registros de Trough

  • ver si las funciones se denominan con los argumentos correctos afirma que utiliza y un depurador.
  • ver si la función devuelve los resultados correctos, escribe unittests.
+0

¿Podrías elaborar/dar enlaces en (1), por favor? –

+2

Gracias por la útil función zip. Creo que tienes razón para la metacrítica. Desarrollo en una máquina remota y no puedo usar un depurador. Entonces, este mecanismo de registro debería ser una forma de eliminar errores de una manera fácil. – Luca

1

Todo está bien en su función. Parece que se pierde con argumentos de palabra clave posicionales vs variables &.

Me explico: argumentos posicionales, a y b en su caso, son obligatorios (y pueden tener valores por defecto). Otros argumentos son opcionales Si desea que un argumento sea obligatorio o que tenga un valor predeterminado, colóquelo antes de * args y ** kwargs. Pero recuerda que no se puede suministrar un argumento doble:

def x(a = 1, b = 2, *args, **kwargs): 
    print a, b, args, kwargs 

>>> x(3, 4, 5, b=6) 
TypeError: x() got multiple values for keyword argument 'b' 

Hay otra manera, pero no es tan fácil de leer, tener valores por defecto de los argumentos y no tienen argumentos posicionales:

def x(*args, **kwargs): 
    kwargs.updae({'a': 1, 'b': 2}) 

Su función que analiza los argumentos está bien, aunque no entiendo por qué escribe varargs y keywords en _.Se pasa argumentos de forma transparente:

def x(a = 1, b = 2, *args, **kwargs): 
    print a, b, args, kwargs 

def y(*args, **kwargs): 
    x(*args, **kwargs) 

>>> y(3, 4, 5, 6) 
3 4 (5, 6) {} 

>>> y(3, 4, 5, b=6) 
TypeError: x() got multiple values for keyword argument 'b' 
0

He encontrado su solución ligeramente agradable puede ser mejorado, si se toma en cuenta que una función general, teóricamente, puede devolver un iterable, en cuyo caso se genera un error.

Aquí es una solución para esto:

Wrap print 'return: %s' % ret en una sentencia if:
if hasattr(ret, "__iter__"): print 'returned iterable' else: print 'return: %s' % ret

De esta manera no se utiliza ya sea mucho tiempo la impresión de grandes iterables, pero eso puede, por supuesto, modificarse según las necesidades. (También una cadena no tiene un atributo __iter__, que es útil)

Cuestiones relacionadas