我编写了这个装饰器,将中间结果保存在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
答案 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.parameters
是parameter_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