2017-08-24 44 views
2

给定一个函数的参数a和其它两个参数(pickle_frompickle_to),我想:输入/输出装饰器泡菜函数结果

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

使用单一功能很简单。如果pickle_from不为null,则该函数仅加载pickled结果并将其返回。否则,它执行一些耗时的计算,使用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 

这可能吗?关于装饰者的事情让我的头部爆炸,所以我会礼貌地拒绝在这里发表“我试过的东西”。

+1

不应该把pickle_from,pickle_to和pathname作为装饰器的参数吗?为什么'somefunc'有他们?无论你想要什么,你都可以拥有它们,但它会影响装饰者必须编码的方式。 –

+0

我认为他们应该在函数本身中,因为它们被设计为由用户而不是代码本身指定。但是,因此我的问题 –

+0

@很好,坚持下去。如果它们被赋予装饰器,那么在调用该函数时无法改变行为。它会永远或永不加载该值。 – mwchase

回答

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

@BradSolomon functools.wraps应该修复可检测性。请参阅https://docs.python.org/3.6/library/functools.html#functools.wraps – mwchase

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

这避免了添加装饰者到处都想使用此功能,实际上可以与任何可调用的函数(不仅仅是函数)一起使用,也可以用于代码。

+0

where'res = pickle_call(somefunc ...'是在'somefunc'的主体中指定的吗?对不起,继续你到那里。 –

+1

@BradSolomon不,对不起,第二个片段是为了展示你将如何使用包装当你需要调用其中一个函数时,我假设你已经定义了一个函数'somefunc',它接收一个参数'a'(就像你的例子),然后直接调用'somefunc',你可以调用'pickle_call'(或者你想命名包装器)并且传递'somefunc'作为第一个参数,接着是函数的常用参数,加上'pickle_from' /'pickle_to';然后'pickle_call'只在必要时调用传递的函数。 – jdehesa