gpt4 book ai didi

python - 重新分配函数属性使其成为 'unreachable'

转载 作者:太空狗 更新时间:2023-10-29 21:34:20 26 4
gpt4 key购买 nike

我有一个简单的小装饰器,它将函数调用的结果作为函数属性缓存在 dict 中。

from decorator import decorator
def _dynamic_programming(f, *args, **kwargs):
try:
f.cache[args]
except KeyError:
f.cache[args] = f(*args, **kwargs)
return f.cache[args]

def dynamic_programming(f):
f.cache = {}
return decorator(_dynamic_programming, f)

我现在想添加清空缓存的可能性。所以我像这样更改 dynamic_programming() 函数:

def dynamic_programming(f):
f.cache = {}
def clear():
f.cache = {}
f.clear = clear
return decorator(_dynamic_programming, f)

现在假设我使用这个小东西来实现斐波那契数列函数:

@dynamic_programming
def fib(n):
if n <= 1:
return 1
else:
return fib(n-1) + fib(n-2)

>>> fib(4)
5
>>> fib.cache
{(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5}

但是现在当我清除缓存时会发生一些奇怪的事情:

>>> fib.clear()
>>> fib.cache
{(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5}

或者(使用新的 Python 内核运行)反过来:

>>> fib.clear()
>>> fib(4)
5
>>> fib.cache
{}

为什么缓存在第一次访问后无法“访问”,即在调用之后调用 clear() 或在 clear() ?

(顺便说一句。我知道一个正确清除缓存的解决方案:调用 f.cache.clear() 而不是将 {} 分配给它按预期工作。我我只对分配解决方案失败的原因感兴趣。)

最佳答案

问题出在 decorator 模块上。如果您向装饰器添加一些 print 语句:

from decorator import decorator
def _dynamic_programming(f, *args, **kwargs):
print "Inside decorator", id(f.cache)
try:
f.cache[args]
except KeyError:
f.cache[args] = f(*args, **kwargs)
return f.cache[args]

def dynamic_programming(f):
f.cache = {}
print "Original cache", id(f.cache)
def clear():
f.cache = {}
print "New cache", id(f.cache)
f.clear = clear
return decorator(_dynamic_programming, f)

@dynamic_programming
def fib(n):
if n <= 1:
return 1
else:
return fib(n-1) + fib(n-2)

print fib(4)
print id(fib.cache)
fib.clear()
print id(fib.cache)
print fib(10)
print id(fib.cache)

它输出(跳过重复行):

Original cache 139877501744024
Inside decorator 139877501744024
5
139877501744024
New cache 139877501802208
139877501744024
Inside decorator 139877501802208
89
139877501744024

如您所见,装饰器内的 cache 根据 clear 函数发生变化。但是,从 __main__ 访问的 cache 并没有改变。在装饰器的外部和内部打印 cache 可以提供更清晰的画面(同样,跳过了重复项):

Inside decorator {}
Inside decorator {(1,): 1}
Inside decorator {(2,): 2, (0,): 1, (1,): 1}
Inside decorator {(2,): 2, (0,): 1, (3,): 3, (1,): 1}
5
Outside {(2,): 2, (0,): 1, (3,): 3, (1,): 1, (4,): 5}
Inside decorator {}
Inside decorator {(1,): 1}
Inside decorator {(2,): 2, (0,): 1, (1,): 1}
Inside decorator {(2,): 2, (0,): 1, (3,): 3, (1,): 1}
Inside decorator {(2,): 2, (0,): 1, (3,): 3, (1,): 1, (4,): 5}
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5, (5,): 8}
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5, (5,): 8, (6,): 13}
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5, (5,): 8, (6,): 13, (7,): 21}
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (8,): 34, (3,): 3, (4,): 5, (5,): 8, (6,): 13, (7,): 21}
Inside decorator {(0,): 1, (1,): 1, (2,): 2, (8,): 34, (3,): 3, (9,): 55, (4,): 5, (5,): 8, (6,): 13, (7,): 21}
89
Outside {(2,): 2, (0,): 1, (3,): 3, (1,): 1, (4,): 5}

如您所见,内部的变化并没有反射(reflect)在外部。问题是里面 the decorator module ,有一行(在它用来制作装饰器的类中):

self.dict = func.__dict__.copy()

然后 later :

func.__dict__ = getattr(self, 'dict', {})

所以基本上,外部的 __dict__ 与内部的 __dict__ 是不同的。这意味着:

  • 装饰器复制(未引用)__dict__
  • 缓存发生变化时,它会改变内部的__dict__,而不是外部的__dict__
  • 因此,_dynamic_programming 使用的缓存 被清除了,但是从外面看不到,因为装饰器的__dict__ 仍然存在指向旧的 cache(正如你在上面看到的,随着内部 cache 更新,而外部 cache 保持不变)

所以,总而言之,这是 decorator 模块的问题。

关于python - 重新分配函数属性使其成为 'unreachable',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26636105/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com