Skip to main content
 首页 » 编程设计

python之内存装饰器无法内存(当不使用装饰器语法时)

2025年05月04日15birdshome

我有一个简单的内存器装饰器:

def funcmemo(f): 
    memo = {} 
    @wraps(f) 
    def wrapper(*args): 
    if args in memo: 
        return memo[args] 
    else: 
        temp = f(*args) 
        print "memoizing: ", args, temp   
        memo[args] = temp 
        return temp 
    return wrapper 

现在,当我通过“@”标记使用它时,

@funcmemo 
def fib(n): 
    print "fib called with:", n 
    if n < 2: return n 
    return fib(n-2) + fib(n-1) 
 
res = fib(3) 
print "result:", res 

如打印输出所示,它工作正常:

fib called with: 3 
fib called with: 1 
memoizing:  (1,) 1 
fib called with: 2 
fib called with: 0 
memoizing:  (0,) 0 
memoizing:  (2,) 1 
memoizing:  (3,) 2 
result:  2 

但是,当我这样做时:

def fib(n): 
    print "fib called with:", n 
    if n < 2: return n 
    return fib(n-2) + fib(n-1) 
 
memfib = funcmemo(fib) 
res = memfib(3) 
print "result:", res 

显然调用了一个未修饰的 fib,只有最终返回值“到达”缓存(显然导致了巨大的减速):

fib called with: 3 
fib called with: 1 
fib called with: 2 
fib called with: 0 
fib called with: 1 
memoizing:  (3,) 2 
result: 2 

奇怪的是,这个工作正常:

def fib(n): 
    print "fib called with:", n 
    if n < 2: return n 
    return fib(n-2) + fib(n-1) 
 
fib = funcmemo(fib) 
res = fib(3) 
print "result:", res 

此外,基于类的版本也会发生同样的事情:

class Classmemo(object): 
    def __init__ (self, f): 
        self.f = f 
        self.mem = {} 
    def __call__ (self, *args): 
        if args in self.mem: 
            return self.mem[args] 
        else: 
            tmp = self.f(*args) 
            print "memoizing: ", args, temp 
            self.mem[args] = tmp 
            return tmp 

使用“匿名”装饰函数时也会出现此问题,例如

res = Classmemo(fib)(3) 

我很乐意了解这背后的原因。

请您参考如下方法:

这没什么奇怪的。当你做的时候

memofib = funcmemo(fib) 

您不会以任何方式更改 fib 指向的函数,而是创建一个新函数 并将名称 memofib 指向它.

因此,当 memofib 被调用时,它会调用名称为 fib 的函数——递归调用自身,而不是 memofib - - 所以不会发生内存。

在你的第二个例子中,你做

fib = funcmemo(fib) 

因此它递归地调用自己,并且在所有级别发生内存。

如果您不想像装饰器版本或您的第二个示例那样覆盖名称 fib,您可以更改 fib 以采用函数名称:

def fib(n, fibfunc): 
    print "fib called with:", n 
    if n < 2: return n 
    return fibfunc(n-2, fibfunc) + fibfunc(n-1, fibfunc) 
 
memofib = funcmemo(fib) 
res = fib(3, memofib) 

你也可以使用 fixed point combinator避免每次都传递 fibfunc:

def Y(f): 
    def Yf(*args): 
        return f(Yf)(*args) 
    return f(Yf) 
 
@Y 
def fib(f): 
    def inner_fib(n): 
        print "fib called with:", n 
        if n < 2: return n 
        return f(n-2) + f(n-1) 
    return inner_fib