2011-05-16 42 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