我调整了一个示例,并提出了以下建议:
I've tweaked an example I saw and came up with the following:
import functools
class Memoized(object):
"""Decorator that caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned, and
not re-evaluated.
"""
__cache = {}
def __init__(self, func):
self.func = func
self.key = (func.__module__, func.__name__)
def __call__(self, *args):
try:
return Memoized.__cache[self.key][args]
except KeyError:
value = self.func(*args)
Memoized.__cache[self.key] = {args : value}
return value
except TypeError:
# uncachable -- for instance, passing a list as an argument.
# Better to not cache than to blow up entirely.
return self.func(*args)
def __get__(self, obj, objtype):
"""Support instance methods."""
return functools.partial(self.__call__, obj)
@staticmethod
def reset():
Memoized.__cache = {}
我的问题是缓存部分似乎涉及很多开销(例如,递归函数).以下面的函数为例,我可以在非记忆版本中调用fib(30)十次,而花的时间少于记忆版本.
My problem with it is that the caching part seems to involve a lot of overhead (eg. for recursive functions). Using the following function as an example, I can call fib(30) ten times with the non-memoized version in less time than the memoized version.
def fib(n):
if n in (0, 1):
return n
return fib(n-1) + fib(n-2)
有人可以建议一种更好的方式来编写此装饰器吗? (或将我指向可以完成我想要的更好(即更快)的装饰器).
我对保留方法签名或帮助自省工具不知道"有关修饰后的功能一无所知.
Can anyone suggest a better way to write this decorator? (or point me to a better (ie. faster) decorator that does what I want).
I'm not interested in preserving method signatures, or helping introspection tools "know" anything about the decorated function.
谢谢.
P.S.使用python 2.7
P.S. Using python 2.7
推荐答案
您实际上并没有在缓存任何数据,因为每次您设置新的缓存值时,都会覆盖以前的值:
You're not actually caching any data, because each time you set a new cached value you overwrite the previous:
Memoized.__cache[self.key] = {args : value}
例如
import functools
class Memoized(object):
"""Decorator that caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned, and
not re-evaluated.
"""
cache = {}
def __init__(self, func):
self.func = func
self.key = (func.__module__, func.__name__)
self.cache[self.key] = {}
def __call__(self, *args):
try:
return Memoized.cache[self.key][args]
except KeyError:
value = self.func(*args)
Memoized.cache[self.key][args] = value
return value
except TypeError:
# uncachable -- for instance, passing a list as an argument.
# Better to not cache than to blow up entirely.
return self.func(*args)
def __get__(self, obj, objtype):
"""Support instance methods."""
return functools.partial(self.__call__, obj)
@staticmethod
def reset():
Memoized.cache = {}
- 未缓存的fib(30):2.86742401123
具有缓存的- fib(30):0.000198125839233
- fib(30) without caching: 2.86742401123
- fib(30) with caching: 0.000198125839233
其他一些注意事项:
- 不要使用
__prefix
;没有理由在这里,它只会使代码丑陋.
- 不是使用单个的,整体的,类属性字典,而是为
Memoized
的每个实例赋予其自己的字典,并保留Memoized
对象的注册表.这样可以改善封装,并消除依赖于模块和函数名称的怪异现象.
- Don't use
__prefix
; there's no reason to here and it only uglifies the code.
- Instead of using a single, monolithic, class-attribute dict, give each instance of
Memoized
its own dict, and keep a registry of Memoized
objects. This improves encapsulation, and removes the oddity of depending on the module and function names.
.
import functools
import weakref
class Memoized(object):
"""Decorator that caches a function's return value each time it is called.
If called later with the same arguments, the cached value is returned, and
not re-evaluated.
>>> counter = 0
>>> @Memoized
... def count():
... global counter
... counter += 1
... return counter
>>> counter = 0
>>> Memoized.reset()
>>> count()
1
>>> count()
1
>>> Memoized.reset()
>>> count()
2
>>> class test(object):
... @Memoized
... def func(self):
... global counter
... counter += 1
... return counter
>>> testobject = test()
>>> counter = 0
>>> testobject.func()
1
>>> testobject.func()
1
>>> Memoized.reset()
>>> testobject.func()
2
"""
caches = weakref.WeakSet()
def __init__(self, func):
self.func = func
self.cache = {}
Memoized.caches.add(self)
def __call__(self, *args):
try:
return self.cache[args]
except KeyError:
value = self.func(*args)
self.cache[args] = value
return value
except TypeError:
# uncachable -- for instance, passing a list as an argument.
# Better to not cache than to blow up entirely.
return self.func(*args)
def __get__(self, obj, objtype):
"""Support instance methods."""
return functools.partial(self.__call__, obj)
@staticmethod
def reset():
for memo in Memoized.caches:
memo.cache = {}
if __name__ == '__main__':
import doctest
doctest.testmod()
添加测试,并使用weakref.WeakSet.请注意,WeakSet仅在2.7(OP正在使用)中可用.有关适用于2.6的版本,请参见编辑历史记录.
Edited: add tests, and use weakref.WeakSet. Note that WeakSet is only available in 2.7 (which the OP is using); for a version that works in 2.6, see the edit history.
这篇关于如何加快此备忘录装饰器的速度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!