2012-04-04 60 views
7

我一直在使用memoization的这个优秀的装饰,这是我在网上找到(与斐波那契序列如下所示为例):了解参数在Python记忆化装饰处理

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) 

现在我为了更好地理解内部工作,我没有通过阅读Python中的装饰器或参数处理来找到答案。

为什么缓存字典在每次调用装饰函数时都不会重新初始化?

x *如何被识别为发送到装饰函数的参数,即函数调用fib(969)中的969?

回答

9

修饰器仅在首次定义修饰函数之后被调用一次。因此,这两种技术(使用@wrap和巴=涡卷(巴))是相同的:

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

在两种情况下很明显,ARR创建只有当涡卷(F)被调用,并且是包装只有当foo和bar第一次被声明时才被调用。

至于将参数传递给装饰函数的情况,请记住装饰器将函数作为参数并返回该函数的修改版本。所以装饰器通常需要一个参数,这是它正在修改的函数。它返回一个新的函数,装饰器可以定义它返回的函数作为任意数量的参数(例如* args)。装饰器甚至可以返回一个函数,它为装饰的方法带来太多参数。

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

虽然最终baz抛出一个错误,请注意如何在抛出错误之前正确打印参数的数量。

+0

+1很好的答案。 – 2012-04-04 12:51:32

+1

+1,有趣的是提到正在创建闭包,这就是为什么当函数defm已经被返回时可以访问'cache'。 – mmarinero 2012-04-04 13:02:33

+0

谢谢,现在我理解装饰器好多了! :) – 2012-04-05 07:14:34