我已经阅读了一些关于装饰器的文章,但我的小脑袋并没有完全理解它们,但我相信这是它们有用的情况之一。
我有一个运行其他一些方法的主要方法:
def run_pipeline():
gene_sequence_fasta_files_made = create_gene_sequence_fasta_files()
....several other methods taking one input argument and having one output argument.
由于每个方法都需要很长时间才能运行,所以我想将每个方法的结果存储在一个 json 对象中。如果 json 文件存在,我加载它,否则我运行该方法并存储结果。我当前的解决方案如下所示:
def run_pipeline():
gene_sequence_fasta_files_made = _load_or_make(create_gene_sequence_fasta_files, "/home/myfolder/ff.json", method_input=None)
...
问题是,我发现这真的很难看而且难以阅读。如果可能,我将如何使用装饰器来解决这个问题?
附言。抱歉没有展示我的尝试。我没有尝试过任何事情,因为我正在为客户的最后期限工作并且没有时间(我可以提供上面的代码;我只是觉得它在美学上令人不快)。
附言。 _load_or_make() 的定义附加:
def _load_or_make(method, filename, method_input=None):
try:
with open(filename, 'r') as input_handle:
data = json.load(input_handle)
except IOError:
if method_input == None:
data = method()
else:
data = method(method_input)
with open(filename, 'w+') as output_handle:
json.dump(data, output_handle)
return data
请您参考如下方法:
这是一个尝试从给定的 filename 加载 json 的装饰器,如果找不到文件或 json 加载失败,它会运行原始函数,将结果作为 json 写入磁盘, 并返回。
def load_or_make(filename):
def decorator(func):
def wraps(*args, **kwargs):
try:
with open(filename, 'r') as f:
return json.load(input_handle)
except Exception:
data = func(*args, **kwargs)
with open(filename, 'w') as out:
json.dump(data, out)
return data
return wraps
return decorator
@load_or_make(filename)
def your_method_with_arg(arg):
# do stuff
return data
@load_or_make(other_filename)
def your_method():
# do stuff
return data
请注意,这种方法存在一个问题:如果装饰方法根据传递给它的参数返回不同的值,缓存将无法正常运行。看起来这不是您的要求,但如果是,您需要根据传入的参数选择不同的文件名(或使用基于 pickle 的序列化,然后只是 pickle args 的字典 -> 结果)。这是一个如何使用 pickle 方法(非常)松散地基于 memoized decorator 的示例Christian P. 链接到:
import pickle
def load_or_make(filename):
def decorator(func):
def wrapped(*args, **kwargs):
# Make a key for the arguments. Try to make kwargs hashable
# by using the tuple returned from items() instead of the dict
# itself.
key = (args, kwargs.items())
try:
hash(key)
except TypeError:
# Don't try to use cache if there's an
# unhashable argument.
return func(*args, **kwargs)
try:
with open(filename) as f:
cache = pickle.load(f)
except Exception:
cache = {}
if key in cache:
return cache[key]
else:
value = func(*args, **kwargs)
cache[key] = value
with open(filename, "w") as f:
pickle.dump(cache, f)
return value
return wrapped
return decorator
在这里,我们没有将结果保存为 json,而是将结果 pickle 为字典中的值,其中键是提供给函数的参数。请注意,您仍然需要为每个修饰的函数使用不同的文件名,以确保您永远不会从缓存中获得不正确的结果。
