2012-04-04 19 views
7

Tôi đã được sử dụng trang trí này tuyệt vời cho memoization, mà tôi tìm thấy trên web (hiển thị ở đây với dãy Fibonacci là một ví dụ):Hiểu xử lý trong một trang trí python memoization tham số

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) 

Bây giờ tôi muốn muốn hiểu các hoạt động bên trong tốt hơn một chút, tôi đã không tìm thấy câu trả lời bằng cách đọc lên các trang trí hoặc xử lý tham số bằng Python.

Tại sao từ điển bộ nhớ cache không được khởi động lại mỗi khi chức năng trang trí được gọi?

Làm thế nào * x được nhận dạng là thông số được gửi đến chức năng được trang trí, tức là 969 trong hàm gọi fib (969)?

Trả lời

9

Trình trang trí chỉ được gọi một lần, ngay sau khi hàm trang trí được xác định lần đầu tiên. Như vậy, hai kỹ thuật này (sử dụng @wrap và thanh = bọc (bar)) đều giống nhau:

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

Trong cả hai trường hợp rõ ràng là arr được tạo ra chỉ khi quấn (f) được gọi, và bọc là chỉ được gọi khi foo và bar được khai báo lần đầu tiên.

Đối với trường hợp chuyển đối số cho hàm được trang trí, hãy nhớ rằng người trang trí cần có một hàm làm thông số và trả về phiên bản đã sửa đổi của hàm đó. Vì vậy, một trang trí thường lấy một tham số, đó là chức năng mà nó đang sửa đổi. Nó trả về một hàm mới, và trình trang trí có thể định nghĩa hàm mà nó trả về khi lấy bất kỳ số đối số nào (ví dụ, * args). Trình trang trí thậm chí có thể trả về một hàm có quá nhiều tham số cho phương thức mà nó trang trí.

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

Trong khi cuối cùng baz ném lỗi, hãy chú ý số lượng đối số được in chính xác trước khi lỗi được ném.

+0

+1 câu trả lời tuyệt vời. –

+1

+1 quá, nó sẽ là thú vị để đề cập đến một đóng cửa đang được tạo ra và đó là lý do tại sao nó có thể truy cập 'cache' khi chức năng' defm' đã được trả lại. – mmarinero

+0

Cảm ơn bạn, bây giờ tôi hiểu trang trí tốt hơn nhiều! :) –

Các vấn đề liên quan