2011-05-16 39 views
4

我会如何写这样的装饰。我希望能够在我调用装饰器时指定max_hits的值(或者可以将其忽略)。Python - 如何为基于类的装饰器指定可选参数?

例如,所期望的用途将是

@memoize(max_hits=7) 
def a(val): 
    print val 

@memoize 
def a(val): 
    print val 

(使用第一实施例给出了关于不正确的参数的误差。)

装饰:

class memoize: 
    """A decorator to cache previosly seen function inputs. 

    usage: 
     @memoize 
     def some_func(.. 
    """ 
    def __init__(self, function, max_hits=None): 
     self.max_hits = max_hits 
     self.function = function 
     self.memoized = {} 

    def __call__(self, *args, **kwargs): 
     key = (args,tuple(kwargs.items())) 
     try: 
      return self.memoized[key] 
     except KeyError: 
      self.memoized[key] = self.function(*args,**kwargs) 
     return self.memoized[key] 
+0

为什么每个人都拼写它的记忆。 – 2011-05-16 13:56:41

+0

@Jakob Bowyer:http://en.wikipedia.org/wiki/Memoization#Etymology – mouad 2011-05-16 13:59:05

+0

[在Python中用可选参数定义一个类]的可能重复(https://stackoverflow.com/questions/10681038/define-a -class-with-optional-argument-in-python) – 2017-10-20 13:28:20

回答

8

您必须制作memoize a 函数,该函数采用可选参数max_hits并返回装饰器(即装饰器)。另一个可以调用的函数作为第一个参数);在这种情况下,你可以使用以下两种语法:

@memoize() 
def func(x): 
    [...] 

@memoize(max_hits=7) 
def func(x): 
    [...] 

所以,大概沿着线的东西:

def memoize(max_hits=None): 
    """Returns a decorator to cache previosly seen function inputs. 

    usage: 
    @memoize() 
    def some_func(.. 
    """ 
    class decorator: 
     def __init__(self, function): 
      self.max_hits = max_hits 
      self.function = function 
      self.memoized = {} 

     def __call__(self, *args, **kwargs): 
      key = (args,tuple(kwargs.items())) 
      try: 
       return self.memoized[key] 
      except KeyError: 
       self.memoized[key] = self.function(*args,**kwargs) 
      return self.memoized[key] 

    return decorator 

注意@memoize()将工作,但您最初期望@memoize语法不会;在后一种情况下,您用@memoize装饰的功能将作为第一个参数传递给memoizemax_hits)。如果你要正确对待这种情况下,可以延长memoize如下:

def memoize(max_hits=None): 
    if callable(max_hits): 
     # For sake of readability... 
     func = max_hits 
     decorator = memoize(max_hits=None) 
     return decorator(func) 
    [...original implementation follows here...] 
+0

所以我的装饰器会返回类? – Greg 2011-05-16 13:54:42

+0

是的;看到我上面的扩展示例。 – 2011-05-16 13:55:05

3

如果您使用3.2+,不要把它写自己,用from functools import lru_cache代替

如果你想支持没有括号的版本,最好为函数参数使用一个标记值,而不是内省“错误”的参数。那就是:

class Memoized(object): 
    # As per the memoize definition in the question 

# Leave out the "*, " in 2.x, since keyword-only arguments are only in 3.x 
from functools import partial 
_sentinel = object() 
def memoize(_f=_sentinel, *, max_hits=None): 
    if _f is _sentinel: 
     return partial(Memoized, max_hits=max_hits) 
    return Memoized(_f, max_hits=max_hits) 
+1

如果你打算降低某些东西,说* why为礼貌。这里描述的定点技术是允许单个函数同时作为装饰器工厂('@memoize(max_hits = 7)'或'@memoize()')和装饰器('@ memoize')的最稳健的方法。 – ncoghlan 2011-07-28 22:36:17