【问题标题】:Input/output decorator to pickle function result输入/输出装饰器到泡菜函数结果
【发布时间】:2018-02-02 14:42:28
【问题描述】:

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

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

只有一个函数,这很简单。如果pickle_from 不为空,该函数只会加载腌制结果并返回它。否则,它会使用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的函数形成一个外壳,从而减少过程中的源代码。

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

@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

这可能吗?关于装饰器的一些事情让我头疼,所以我会礼貌地拒绝在这里发布“我尝试过的东西”。

【问题讨论】:

  • 不应该pickle_frompickle_topathname是装饰器的参数吗?为什么somefunc 有它们?你可以随心所欲地拥有它们,但这会影响装饰器的编码方式。
  • 我认为它们应该在函数本身中,因为它们被设计为由用户指定,而不是在代码本身中。但因此我的问题
  • @Rawing 好,等一下。如果将它们提供给装饰器,则在调用函数时无法更改行为。它要么总是加载,要么永远不加载值。
  • @mwchase 正确,这就是我想说的。
  • somefunc 会使用pickle_frompickle_to 参数吗?你可以让装饰器提供这些参数,然后你就不必每次都定义它们。

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


【解决方案1】:

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

【讨论】:

【解决方案2】:

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

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

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

【讨论】:

  • res = pickle_call(somefunc...somefunc 的正文中指定的位置在哪里?抱歉,一直跟着你。
  • @BradSolomon 不,抱歉,第二个 sn-p 旨在展示当您需要调用其中一个函数时如何使用包装器。我假设您已经定义了一个函数somefunc,它接收一个参数a(就像在您的示例中一样)。然后,代替直接调用somefunc,您将调用pickle_call(或者您想要命名包装器)并将somefunc作为第一个参数传递,然后是函数的常用参数,加上pickle_from/@987654331 @ 有必要的;然后pickle_call 仅在必要时调用传递的函数。
猜你喜欢
  • 2017-01-09
  • 1970-01-01
  • 2023-03-21
  • 2022-06-28
  • 2019-01-25
  • 1970-01-01
  • 2012-02-21
  • 2012-05-13
  • 1970-01-01
相关资源
最近更新 更多