hil*_*sia 7 python function-attributes
我有一个简单的小装饰器,它将函数调用的结果缓存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)
Run Code Online (Sandbox Code Playgroud)
我现在想添加清空缓存的可能性.所以我改变了dynamic_programming()这样的功能:
def dynamic_programming(f):
f.cache = {}
def clear():
f.cache = {}
f.clear = clear
return decorator(_dynamic_programming, f)
Run Code Online (Sandbox Code Playgroud)
现在让我们假设我使用这个小东西来实现Fibonacci数函数:
@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}
Run Code Online (Sandbox Code Playgroud)
但是现在当我清除缓存时会发生一些奇怪的事情:
>>> fib.clear()
>>> fib.cache
{(0,): 1, (1,): 1, (2,): 2, (3,): 3, (4,): 5}
Run Code Online (Sandbox Code Playgroud)
或者(运行新的Python内核)反过来做:
>>> fib.clear()
>>> fib(4)
5
>>> fib.cache
{}
Run Code Online (Sandbox Code Playgroud)
为什么缓存在第一次访问后会以某种方式"无法访问",即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)
Run Code Online (Sandbox Code Playgroud)
它输出(跳过重复的行):
Original cache 139877501744024
Inside decorator 139877501744024
5
139877501744024
New cache 139877501802208
139877501744024
Inside decorator 139877501802208
89
139877501744024
Run Code Online (Sandbox Code Playgroud)
如您所见,cache装饰器内部根据清除功能而变化.但是,cache访问__main__权限不会改变.打印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}
Run Code Online (Sandbox Code Playgroud)
如您所见,内部更改不会在外部回显.的问题是,内部的decorator模块,有线(类内它用于制造装饰):
self.dict = func.__dict__.copy()
Run Code Online (Sandbox Code Playgroud)
再后来:
func.__dict__ = getattr(self, 'dict', {})
Run Code Online (Sandbox Code Playgroud)
所以基本上,__dict__外面__dict__的内部不同.这意味着:
__dict__被复制(未标记)通过该装饰cache变化时,它会改变内部__dict__,而不是外部__dict__cache所用的_dynamic_programming被清除,但你不能看到,从外面看,作为装饰的__dict__仍然指向旧的cache(你可以在上面看到,随着内cache更新,而外界cache保持不变)总而言之,这是decorator模块的一个问题.
| 归档时间: |
|
| 查看次数: |
128 次 |
| 最近记录: |