设计能够计算函数或从文件加载结果的应用程序

Design for application able to calculate a function or load the result from a file

我有很多文件需要用某些耗时的函数处理 f_1() ... f_n()。 因此,每次函数 f_k() 结束其工作时,我都会将 fk_processed_output 保存在文件中,以避免在未来的工作会话中重新计算。

程序必须能够动态选择是否需要计算结果或从 HDD 加载结果(如果给定的输入文件是在之前计算的)。

实现这个的好的设计是什么?

我想像这样实现每个 f_k{}:

def f_k():
    if (fk_processed_output.exist()):
        load_it_from_file()
    else:
        output = do_some_stuff()
    save_to_file(output)

但我不太喜欢它,因为每个函数都重复了 if/else 模式。

有什么更好或更聪明的主意吗?还有其他提示吗?是否存在用于此目的的图书馆?

使用装饰器:

def fk_processed (fk):
    filename = fk.__name__ + '_output.json'
    def new_fk (*args, **kargs):
        if (os.path.exists(filename)):
            print("Load from file (" + fk.__name__ + ").")
            output = json.load (open(filename))
        else:
            print("Call to function (" + fk.__name__ + ").")
            output = fk (*args, **kargs)
            json.dump (output, open(filename, 'w'))
        return output
    return new_fk

@fk_processed
def f1 (): 
    ...

@fk_processed
def f2 ():
    ...

>>> f1 ()
Call to function (f1).
>>> f1 ()
Load from file (f1).

我使用 json 存储数据,因此您(大多数时候)不必担心 fk.

返回的值的类型

请注意,如果您的 fk 的输出不总是相同,这将不起作用。在这种情况下,您必须向 fk_processed.

添加一些额外的内容

这是一个适用于参数但不适用于关键字参数的版本(您可以根据需要修改它,只需始终以相同的顺序考虑关键字参数...)。没有很重的功能就不要用!

def fk_processed (fk):
    filename = fk.__name__ + '_output.json'
    def new_fk (*args):
        store = {}
        if (os.path.exists(filename)):
            store = json.load (open(filename))
        cstore = store
        i = 0
        while i < len(args) and str(args[i]) in cstore:
            cstore = cstore[str(args[i])]
            i += 1
        if i != len(args):
            while i < len(args) - 1:
                cstore[str(args[i])] = {}
                cstore = cstore[str(args[i])]
                i += 1
            # print('compute')
            cstore[str(args[i])] = fk (*args)
            cstore = cstore[str(args[i])]
            json.dump (store, open(filename, 'w'))
        return cstore
    return new_fk

请注意,我正在将 store 字典中的参数转换为字符串,因为 JSON 只允许字符串作为键。如果您的函数参数不简单,这可能会破坏很多事情。

示例:

>>> @fk_processed
... def add (x, y): return x + y
...
>>> add (1, 2)
compute
3
>>> add (1, 3)
compute
4
>>> add (1, 2)
3
>>> add ('1', '3') # Here is the problem if you can have various types from your arguments
4 # expected '13'