Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
python decorator参数使用函数包装中的变量_Python_Python Decorators - Fatal编程技术网

python 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 目标是编写一个

我正在寻找一种方法来创建一个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
目标是编写一个缓存包装器。装饰器将需要缓存键,但该键将包含传递到函数中的变量,并且对于它包装的每个函数都是不同的

理想情况下,这允许装饰程序检查给定键的缓存值,如果未找到该值,则执行函数以获取该值并缓存该值。这样,如果值在缓存中,则不会执行创建值的代码(即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

这是一个缓存系统,它正是这样做的

你可以用狗堆。如果没有,您可以查看它的源代码以了解它是如何工作的

顺便说一句,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,这样就可以再次传递它们,这是相当聪明的。最后,由于需要大量的“额外”信息,我选择了另一个方向,这些信息可以更好地定义“值函数”以传递到我的方法中,而不是使用装饰器