繁体   English   中英

了解 python 记忆装饰器中的参数处理

[英]Understanding parameter handling in a python memoization decorator

我一直在使用这个出色的装饰器来进行记忆,我在 web 上找到了它(此处以斐波那契数列为例):

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 中的装饰器或参数处理找到答案。

为什么每次调用修饰的 function 时都不会重新初始化缓存字典?

function 调用 fib(969) 时,*x 是如何被识别为发送给装饰后的 function 的参数,即 969?

装饰器只被调用一次,紧接着被装饰的 function 被第一次定义。 因此,这两种技术(使用@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。

至于将 arguments 传递给装饰后的 function 的情况,请记住装饰器将function作为参数并返回该 function 的修改版本。因此,装饰器通常采用一个参数,即它正在修改的 function。 它返回一个新的 function,装饰器可以将它返回的 function 定义为采用任意数量的参数(例如,*args)。 装饰器甚至可以返回一个 function ,它为其装饰的方法采用了太多参数。

>>> 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 会抛出错误,但请注意在抛出错误之前如何正确打印数字 arguments。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM