输入/输出装饰器到pickle功能结果

时间:2017-08-24 16:02:52

标签: python python-3.x decorator pickle python-decorators

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

  • 如果pickle_from不是pickle_from,请加载并返回位于None的腌制对象。如果是None,请计算a的某些函数并将其返回。
  • 如果pickle_to不是pickle_to,则将上述结果转储到None

使用单一功能,这很简单。如果pickle_from不为null,则该函数仅加载pickle结果并返回它。否则,它会使用a执行一些时间密集型计算,将其转储到pickle_to,并返回计算结果。

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

这可能吗?关于装饰器的东西会让我的脑袋爆炸,所以我会礼貌地拒绝在这里发布“我尝试过的东西。”

2 个答案:

答案 0 :(得分:2)

这个装饰者需要一点内省。具体来说,我已使用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

答案 1 :(得分:1)

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

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")

这样可以避免在任何想要使用此功能的地方添加装饰器,并且实际上可以使用代码中的任何可调用(不仅仅是函数)。