在装饰器

时间:2017-06-05 19:21:41

标签: python python-decorators

我编写了这个装饰器,将中间结果保存在json文件中

import json
import os

def json_file(fname):
    def decorator(function):
        def wrapper(*args, **kwargs):
            if os.path.isfile(fname):
                with open(fname, 'r') as f:
                    ret = json.load(f)
            else:
                with open(fname,'w') as f:
                    ret = function(*args, **kwargs)
                    json.dump(ret, f)
            return ret
        return wrapper
    return decorator

用法是

@json_file("cached.json")
def some_calculation(n):
   return {"result": 2**n}

我想增加使用像这样的函数参数:

@json_file("cached_{n}.json")
def calculation(n):
    return {"result": 2**n}

这样在调用函数时{n}会被n的值替换。

我尝试用fname替换fname.format(**kwargs),但没有成功。

如何实现这一目标?

编辑:

根据Per @ jonrshape的评论,这是我在添加.format(**kwargs)后得到的错误

 <ipython-input-4-12b0c894b345> in wrapper(*args, **kwargs)
       5     def decorator(function):
       6         def wrapper(*args, **kwargs):
 ----> 7             fname = fname.format(**kwargs)
       8             if os.path.isfile(fname):
       9                 with open(fname, 'r') as f:

 UnboundLocalError: local variable 'fname' referenced before assignment

2 个答案:

答案 0 :(得分:1)

如果n有时会成为位置,有时候会成为关键字参数,您可以先搜索关键字参数kwargs,如果失败,则“回退”以获取{{1 }}:

args

为了完整起见,我会解决你的错误。

您收到def json_file(fname): def decorator(function): def wrapper(*args, **kwargs): try: value = kwargs['n'] except KeyError: value = args[0] fname.format(value) if os.path.isfile(fname): with open(fname, 'r') as f: ret = json.load(f) else: with open(fname,'w') as f: ret = function(*args, **kwargs) json.dump(ret, f) return ret return wrapper return decorator 错误的原因是因为Python看到了您的变量定义。

如果已在当前范围中定义变量,则将变量分配给新值只需 重新绑定 它到新值。但是,如果尚未定义变量,则Python会将其视为变量 定义 ,而不是重新绑定。

这就是您的代码失败的原因。 Python期望在当前范围中定义UnboundLocalError,而不是父范围。但由于fname从未定义,因此引发了错误。

您可以使用nonlocal语句修复此错误。来自文档:

  

nonlocal语句使列出的标识符引用最近的封闭范围中除了全局变量之前绑定的变量。这很重要,因为绑定的默认行为是首先搜索本地名称空间。除了全局(模块)范围之外,该语句允许封装代码重新绑定局部范围之外的变量。

以下是用法示例:

fname

答案 1 :(得分:1)

您可以使用inspect模块。

首先我们提取包装函数的签名:

signature= inspect.signature(function)

然后我们bind * args和** kwargs:

bound_args= signature.bind(*args, **kwargs)

现在bound_args.parametersparameter_name:parameter_value的词典,我们可以使用它来格式化我们的文件名:

file_name= fname.format(**bound_args.arguments)

一切都放在一起:

import json
import os
import inspect

def json_file(fname):
    def decorator(function):
        signature= inspect.signature(function)

        def wrapper(*args, **kwargs):
            bound_args= signature.bind(*args, **kwargs)
            file_name= fname.format(**bound_args.arguments)

            #~ print(file_name)
            if os.path.isfile(file_name):
                with open(file_name, 'r') as f:
                    ret = json.load(f)
            else:
                with open(file_name,'w') as f:
                    ret = function(*args, **kwargs)
                    json.dump(ret, f)
            return ret
        return wrapper
    return decorator