【问题标题】:Double wrapping Fibonacci双重环绕斐波那契
【发布时间】:2020-01-26 22:37:14
【问题描述】:

我尝试在递归斐波那契函数上使用两个不同的包装器来:1) 计算递归次数,2) 记住计算值以减少所需的计算。

由于每个包装函数都会在创建的函数上创建一个新的函数属性,再次包装后我无法再访问它。 有没有办法在不使用两种效果的单个包装函数的情况下仍然访问它?

def count(f):
    def f1(*args):
        f1.counter += 1
        return f(*args)
    f1.counter = 0
    return f1

def memoize(f):
    def f1(*args):
        if not args in f1.memo:
            f1.memo[args] = f(*args)
        return f1.memo[args]
    f1.memo = {}
    return f1

@memoize
@count
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

之后我可以访问 fib.memo 但不能访问 fib.counter,反之,如果我在 count 之前用 memoize 包装 fib。

【问题讨论】:

标签: python python-3.x python-decorators


【解决方案1】:

这个问题最初是作为 How to make a chain of function decorators? 的副本关闭的,其中正确指出使用 functools.wraps 链接装饰器可以通过将包装函数的所有属性复制到包装函数来提供帮助。

但是,我要重新提出这个问题,因为在您的情况下使用 functools.wraps 将无法完全工作,因为您的 counter 属性是一个不可变的整数,因此让 wrapscounter 属性复制到包装器实际上只是将其初始值 0 复制到包装器中,随后对包装器函数的counter 属性的更改不会反映在包装器的counter 属性中,这就是原因:

from functools import wraps

def count(f):
    @wraps(f)
    def f1(*args):
        f1.counter += 1
        return f(*args)
    f1.counter = 0
    return f1

def memoize(f):
    @wraps(f)
    def f1(*args):
        if not args in f1.memo:
            f1.memo[args] = f(*args)
        return f1.memo[args]
    f1.memo = {}
    return f1

@memoize
@count
def fib(n):
    if n < 2:
        return n
    return fib(n - 1) + fib(n - 2)

print(fib(4))
print(fib.memo)
print(fib.counter)

错误输出:

3
{(1,): 1, (0,): 0, (2,): 1, (3,): 2, (4,): 3}
0

要解决这个问题,您必须使用可变对象初始化counter。由于 Python 本身没有可变整数,因此您可以创建一个包含整数的自定义类:

class Int:
    def __init__(self, value=0):
        self.value = value

def count(f):
    @wraps(f)
    def f1(*args):
        f1.counter.value += 1
        return f(*args)
    f1.counter = Int()
    return f1

这样:

print(fib(4))
print(fib.memo)
print(fib.counter.value)

会正确输出:

3
{(1,): 1, (0,): 0, (2,): 1, (3,): 2, (4,): 3}
5

【讨论】:

    猜你喜欢
    • 2016-04-28
    • 2012-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 2014-05-23
    • 2013-08-09
    相关资源
    最近更新 更多