python decorator参数使用函数包装中的变量
我正在寻找一种方法来创建一个decorator,使其具有一个函数参数,该参数实际使用传递到函数包装中的变量 例如,假设我有python decorator参数使用函数包装中的变量,python,python-decorators,Python,Python Decorators,我正在寻找一种方法来创建一个decorator,使其具有一个函数参数,该参数实际使用传递到函数包装中的变量 例如,假设我有 @cache_decorator("my_key_{}".format(foo)) def my_function(foo, bar): pass @cache_decorator("another_key_{}_{}".format(foo, bar) def another_function(user, foo, bar): pass 目标是编写一个
@cache_decorator("my_key_{}".format(foo))
def my_function(foo, bar):
pass
@cache_decorator("another_key_{}_{}".format(foo, bar)
def another_function(user, foo, bar):
pass
目标是编写一个缓存包装器。装饰器将需要缓存键,但该键将包含传递到函数中的变量,并且对于它包装的每个函数都是不同的
理想情况下,这允许装饰程序检查给定键的缓存值,如果未找到该值,则执行函数以获取该值并缓存该值。这样,如果值在缓存中,则不会执行创建值的代码(即my_函数)。如果未找到,它将执行my_函数并将结果存储在缓存中,并返回结果
另一种选择类似于块:
def my_function(foo, bar):
cache_value("my_key_{}".format(foo),{block of code to generate value that is only called if necessary})
在Objective-C或js中,这将是一个块,因此我可以在本地定义和更改值生成,但仅在必要时执行。我对python太陌生了,无法完全掌握如何使用闭包的verison实现这一点
谢谢
更新虽然下面的解决方案适用于decorators,但我最终选择了类似于块的路线,因为需要附加到每个缓存项以确保它可以正确地无效。通过值生成(而不是在缓存函数中)定义此元数据更易于维护。这看起来像:
def my_function(foo, bar):
def value_func():
return code_to_generate_value_using_foo_bar
return get_set_cache(key, value_func, ...)
def get_set_cache(key, value_function, ...):
value = cache.get(key)
if value is None:
value = value_function()
cache.set(key, value)
return value
在创建装饰器时,可以传递两个列表。第一个将包含位置参数的位置列表,第二个将包含关键字参数的参数名称列表
def cached(positions, names):
def cached_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
keys = [func.__name__] + [str(kwargs.get(name)) for name in sorted(names)] + [str(args[position]) for position in positions]
cache_key = '_'.join(keys)
cached_value = cache.get(cache_key)
if cached_value:
return cached_value
value = func(*args, **kwargs)
cache.set(cache_key, value)
return cached_value
return wrapper
return cached_decorator
你会这样使用它吗
# this will cache the function using b and name parameters
@cached([1], ["name"])
def heavy_calc(a, b, c, name=None):
something_realy_slow()
return answer
问题是,您还应该序列化函数的答案,并在从缓存检索时反序列化。另一个问题是两个不同的调用函数可以给出相同的键(heavy\u calc(“hello\u there”,“foo”)
和heavy\u calc(“hello”,“there\u foo”)
)。解决方案是使用json或msgpack创建args和kwargs的序列化,以便确保键是唯一的
如果您使用的是Python3.3,并且不需要选择要缓存的参数,那么可以使用您是否看到了
dogpile.cache
- 分开保管钥匙
- 序列化/反序列化
- 价值到期和重新验证
- 处理缓存命中和未命中
- 等
@cache_w_keyfunc(lambda foo, bar: (bar,))
def my_function(foo, bar):
pass
@cache_w_keyfunc(lambda user, foo, bar: (foo, bar))
def another_function(user, foo, bar):
pass
键生成器应该返回可散列的内容,例如字符串的元组。如果它们不可散列,比如列表,可以将它们转换为字符串
此键构建函数获取与函数本身相同的参数,并返回要使用的键
def cache_w_keyfunc(keyfunc):
def real_decorator(func):
func.cache = {}
@functools.wraps(func)
def wrapper(*args, **kwargs):
# Create the key now out of the wrapped function's name and the given keys:
key = (func.__name__, keyfunc(*args, **kwargs))
try:
return func.cache[cache_key]
except KeyError:
value = func(*args, **kwargs)
func.cache.set(cache_key, value)
return value
return wrapper
return real_decorator
这是一个有趣的想法。有点复杂,但听起来好像真的没有一个简单的方法来做。看起来狗堆也有同样的问题。最终,这是将变量传递到装饰器的方法。定义一个lamda来获取匹配的arg/kwarg,这样就可以再次传递它们,这是相当聪明的。最后,由于需要大量的“额外”信息,我选择了另一个方向,这些信息可以更好地定义“值函数”以传递到我的方法中,而不是使用装饰器