2012-04-04 19 views
7

Używam tego doskonałą dekorator dla memoization, które znalazłem w internecie (pokazany tutaj z ciągu Fibonacciego jako przykład):Zrozumienie postępowania w dekoratora pyton memoization parametr

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) 

Teraz bym lubię lepiej zrozumieć wewnętrzne działania, nie znalazłem odpowiedzi, czytając o dekoratorach lub obsłudze parametrów w Pythonie.

Dlaczego słownik cache nie jest reinicjowany za każdym razem, gdy wywoływana jest funkcja dekorowania?

W jaki sposób * x jest uznawane za parametry wysyłane do dekorowanej funkcji, tj. 969 w wywołaniu funkcji fib (969)?

Odpowiedz

9

Dekorator wywoływany jest tylko raz, natychmiast po zdefiniowaniu funkcji dekorowania. Tak więc, te dwie techniki (przy użyciu @wrap i bar = zawijania (bar)) są takie same:

>>> 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 

W obydwu przypadkach, oczywiste jest, że ARR powstaje tylko wtedy, gdy okład (F) jest wywoływana i wrap wywoływane tylko wtedy, gdy najpierw deklarowane są foo i bar.

Jeśli chodzi o przekazywanie argumentów do dekorowanej funkcji, pamiętaj, że dekorator przyjmuje jako funkcję parametr i zwraca zmodyfikowaną wersję tej funkcji. Dekorator zazwyczaj przyjmuje jeden parametr, który jest funkcją, którą modyfikuje. Zwraca nową funkcję, a dekorator może zdefiniować zwracaną funkcję jako przyjmującą dowolną liczbę argumentów (na przykład * args). Dekorator może nawet zwrócić funkcję, która ma zbyt wiele parametrów dla dekorowanej metody.

>>> 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 

Podczas gdy bazowy generuje błąd, zauważ, że liczba argumentów jest poprawnie wydrukowana zanim zostanie zgłoszony błąd.

+0

+1 wspaniała odpowiedź. –

+1

+1 również, warto wspomnieć, że tworzone jest zamknięcie i dlatego można uzyskać dostęp do 'cache', gdy funkcja' defm' została już zwrócona. – mmarinero

+0

Dziękuję, teraz rozumiem dekoratorów o wiele lepiej! :) –