Skip to main content
 首页 » 编程设计

python之如果文件存在,使用装饰器检索 jsondata,否则运行方法然后将输出存储为 json

2025年05月04日13itcoder

我已经阅读了一些关于装饰器的文章,但我的小脑袋并没有完全理解它们,但我相信这是它们有用的情况之一。

我有一个运行其他一些方法的主要方法:

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 为字典中的值,其中键是提供给函数的参数。请注意,您仍然需要为每个修饰的函数使用不同的文件名,以确保您永远不会从缓存中获得不正确的结果。