Python - 如何为基于类的装饰器指定可选参数?

时间:2011-05-16 13:49:26

标签: python decorator memoization

我如何写这样的装饰器。我希望能够在调用装饰器时指定max_hits的值(或者可选地将其保留)。

,例如,期望的用途是

@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]

2 个答案:

答案 0 :(得分:8)

你必须使memoize一个带有可选参数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进行装饰的函数将作为第一个参数(memoize)传递给max_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...]

答案 1 :(得分:4)

如果您使用的是3.2+,请不要自行编写,请改用from functools import lru_cache

如果要支持无括号版本,最好使用函数参数的sentinel值,而不是内省“错误”参数。那就是:

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)