我一直在使用这个优秀的装饰器进行记忆,我在网上找到了(这里以Fibonacci序列为例):
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?
答案 0 :(得分:9)
装饰器仅在首次定义修饰函数后立即调用一次。因此,这两种技术(使用@wrap和bar = wrap(bar))是相同的:
>>> 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
在这两种情况下,很明显只有在调用wrap(f)时才会创建arr,并且只有在首次声明foo和bar时才会调用wrap。
对于将参数传递给修饰函数的情况,请记住装饰器将函数作为参数并返回该函数的修改版本。所以装饰器通常需要一个参数,这是它正在修改的函数。它返回一个新函数,装饰器可以定义它返回的函数作为任意数量的参数(例如,* 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会抛出错误,但请注意在抛出错误之前如何正确打印参数的数量。