Input/output pickle 函数结果的装饰器

Input/output decorator to pickle function result

给定一个带有参数 a 和另外两个参数(pickle_frompickle_to)的函数,我想:

使用单个函数,这很简单。如果 pickle_from 不为 null,则该函数只加载腌制结果并 returns 它。否则,它会使用 a 执行一些耗时的计算,将其转储到 pickle_to,然后 return 计算结果。

try:
   import cPickle as pickle
except:
   import pickle

def somefunc(a, pickle_from=None, pickle_to=None):

    if pickle_from:
        with open(pickle_from + '.pickle', 'rb') as f
            res = pickle.load(f)

    else:
        # Re-calcualte some time-intensive func call
        res = a ** 2

    if pickle_to:
        # Update pickled data with newly calculated `res`
        with open(pickle_to + '.pickle', 'wb') as f:
            pickle.dump(res, f)

    return res

我的问题是关于如何构建装饰器,以便此过程可以围绕类似于 somefunc 的多个函数形成 shell,减少程序中的源代码。

我希望能够写出类似这样的东西:

@pickle_option
def somefunc(a, pickle_from=None, pickle_to=None)  
    # or do params need to be in the decorator call?
    # remember, "the files are in the computer"
    res = a ** 2
    return res

这可能吗?关于装饰器的一些事情让我脑袋爆炸,所以我会礼貌地拒绝post这里"what I have tried."

鉴于您的用例,我认为仅使用通用包装器会更清楚:

def pickle_call(fun, *args, pickle_from=None, pickle_to=None, **kwargs):
    if pickle_from:
        with open(pickle_from + '.pickle', 'rb') as f
            res = pickle.load(f)
    else:
        res = fun(*args, **kwargs)
    if pickle_to:
        # Update pickled data with newly calculated `res`
        with open(pickle_to + '.pickle', 'wb') as f:
            pickle.dump(res, f)
    return res

然后你会像这样使用它:

res = pickle_call(somefunc, a, pickle_from="from", pickle_to="to")

这避免了在您要使用此功能的任何地方都必须添加装饰器,并且实际上适用于您的代码或其他任何可调用对象(不仅仅是函数)。

这个装饰器需要一点反省。具体来说,我利用 inspect.Signature 提取了 pickle_frompickle_to 参数。

除此之外,它是一个非常简单的装饰器:它保留对装饰函数的引用,并在必要时调用它。

import inspect
from functools import wraps

def pickle_option(func):
    sig = inspect.signature(func)

    @wraps(func)
    def wrapper(*args, **kwargs):
        # get the value of the pickle_from and pickle_to parameters
        # introspection magic, don't worry about it or read the docs
        bound_args = sig.bind(*args, **kwargs)
        pickle_from = bound_args.arguments.get('pickle_from', \
                             sig.parameters['pickle_from'].default)
        pickle_to = bound_args.arguments.get('pickle_to', \
                             sig.parameters['pickle_to'].default)

        if pickle_from:
            with open(pickle_from + '.pickle', 'rb') as f:
                result = pickle.load(f)
        else:
            result = func(*args, **kwargs)

        if pickle_to:
            with open(pickle_to + '.pickle', 'wb') as f:
                pickle.dump(result, f)

        return result

    return wrapper