2012-04-04 6 views
7

He estado usando este excelente decorador de memoization, que encontré en la web (que se muestra aquí con la secuencia de Fibonacci como ejemplo):en el manejo de un decorador pitón memoization parámetro entendimiento

def memoize(f): 
    cache= {} 
    def memf(*x): 
     if x not in cache: 
      cache[x] = f(*x) 
     return cache[x] 
    return memf 

@memoize 
def fib(n): 
    if n==1 or n==0: 
     return 1 
    return fib(n-2) + fib(n-1) 

print fib(969) 

Ahora me me gusta entender el funcionamiento interno un poco mejor, no he encontrado respuestas leyendo sobre decoradores o manejo de parámetros en Python.

¿Por qué el diccionario de caché no se reinicializa cada vez que se llama a la función decorada?

¿Cómo se reconoce * x los parámetros enviados a la función decorada, es decir, 969 en la función llamada fib (969)?

Respuesta

9

El decorador se invoca una sola vez, inmediatamente después de definir por primera vez la función decorada. Por lo tanto, estas dos técnicas (utilizando @wrap y bar = envoltura (bar)) son los mismos:

>>> def wrap(f): 
...  print 'making arr' 
...  arr = [] 
...  def inner(): 
...   arr.append(2) 
...   print arr 
...   f() 
...  return inner 
...  
>>> @wrap 
... def foo(): 
...  print 'foo was called' 
...  
making arr 
>>> foo() 
[2] 
foo was called 
>>> foo() 
[2, 2] 
foo was called 
>>> def bar(): 
...  print 'bar was called' 
...  
>>> bar = wrap(bar) 
making arr 
>>> bar() 
[2] 
bar was called 

En ambos casos es evidente que se crea arr sólo cuando envoltura (f) se llama, y ​​envoltura es llamado solo cuando foo y bar se declaran por primera vez.

En el caso de pasar argumentos a una función decorada, recuerde que un decorador toma una función como parámetro y devuelve una versión modificada de esa función. Entonces un decorador típicamente toma un parámetro, que es la función que está modificando. Devuelve una nueva función y el decorador puede definir la función que devuelve tomando cualquier cantidad de argumentos (por ejemplo, * args). El decorador puede incluso devolver una función que toma demasiados parámetros para el método que decora.

>>> def wrap_with_arg(f): 
...  def wrap(*args): 
...   print 'called with %d arguments' % len(args) 
...   f(args) 
...  return wrap 
...  
>>> @wrap_with_arg 
... def baz(arg): 
...  print 'called with argument %r' % arg 
...  
>>> baz(3) 
called with 1 arguments 
called with argument 3 
>>> baz(3, 4) 
called with 2 arguments 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
    File "<input>", line 4, in wrap 
    File "<input>", line 3, in baz 
TypeError: not all arguments converted during string formatting 

Aunque finalmente Baz lanza un error, observe cómo el número de argumentos se imprime correctamente antes se lanza el error.

+0

+1 gran respuesta. –

+1

+1 también, sería interesante mencionar que se está creando un cierre y es por eso que es posible acceder a 'caché' cuando la función' defm' ya ha sido devuelta. – mmarinero

+0

¡Gracias, ahora entiendo a los decoradores mucho mejor! :) –