我正在尝试为memoize编写python decorator。 我几乎没有问题。
...
class memoize:
def __init__(self):
self.cache = {}
def __call__(self, function):
def wrapper(*args, **kwargs):
key = str(function.__name__) + str(args) + str(kwargs)
if key in cache:
return cache[key]
else:
value = function(*args, **kwargs)
cache[key] = value
return value
return wrapper
@memoize
def fib(n):
if n in (0, 1):
return 1
else:
return fib(n-1) + fib(n-2)
for i in range(0, 10):
print(fib(i))
我收到了编译错误。
Traceback (most recent call last):
File "memoize.py", line 17, in <module>
@memoize
TypeError: __init__() takes exactly 1 argument (2 given)
答案 0 :(得分:5)
@decorator
只是func = decorator(func)
的语法糖。所以这里有所不同:(1)
@decorator
def func():
...
与
相同func = decorator(func) # Just call of __init__
func(...) # Call of decorator.__call__
但是(2)
@decorator(some_param)
def func():
...
与
类似# Call of __init__ plus call of __call__
func = decorator(some_param)(func)
# Call of closure returned by decorator.__call__
func(...)
您已经为(2)语法实现了装饰器接受参数,但在使用它们时不提供它们,如示例(1)所示。这就是__init__
抱怨的原因,它接收func
作为第二个参数。
您应该在self.cache
闭包中写wrapper
,因此wrapper
会引用相应的decorator
对象。仅写cache
将导致全局变量搜索,因此将失败。
UPD :我将您的代码更改为接近(1):
class memoize:
def __init__(self, function):
self.cache = {}
self.function = function
def __call__(self, *args, **kwargs):
key = str(args) + str(kwargs)
if key in self.cache:
return self.cache[key]
value = self.function(*args, **kwargs)
self.cache[key] = value
return value
@memoize
def fib(n):
if n in (0, 1):
return 1
else:
return fib(n-1) + fib(n-2)
for i in range(0, 10):
print(fib(i))
print(fib.cache)
答案 1 :(得分:0)
This is a custom cache我已经满足了我的特定需求,并提供了一些额外的功能,例如清除缓存和更新密钥等等。
import datetime
class CustomCache:
def __init__(self, function, max_size=100):
"""Constructor"""
self.function = function
self.cache = {}
self.max_cache_size = max_size
def __call__(self, *args, **kwargs):
key = str(args) + str(kwargs)
if key in self.cache:
return self.cache[key]['value']
value = self.function(*args, **kwargs)
self.update(key, value)
return value
def __contains__(self, key):
"""
Returns True or False depending on whether or not the key is in the cache
"""
return key in self.cache
def update(self, key, value):
"""
Update the cache dictionary and optionally remove the oldest item
"""
if key not in self.cache and len(self.cache) >= self.max_cache_size:
self.remove_oldest()
self.cache[key] = {'date_accessed': datetime.datetime.now(), 'value': value}
def update_key(self, value, *args, **kwargs):
"""
Update specific key in cache just in case
"""
key = str(args) + str(kwargs)
if key in self.cache:
self.cache[key] = {'date_accessed': datetime.datetime.now(), 'value': value}
def remove_oldest(self):
"""
Remove the entry that has the oldest accessed date
"""
oldest_entry = None
for key in self.cache:
if oldest_entry is None:
oldest_entry = key
elif self.cache[key]['date_accessed'] < self.cache[oldest_entry]['date_accessed']:
oldest_entry = key
self.cache.pop(oldest_entry)
def cache_clear(self):
"""
Clear entire cache
"""
self.cache = {}
@property
def size(self):
"""
Return the size of the cache
"""
return len(self.cache)