Python类装饰器参数

Dac*_*hmt 14 python arguments decorator

我正在尝试将可选参数传递给python中的类装饰器.在我目前的代码下面:

class Cache(object):
    def __init__(self, function, max_hits=10, timeout=5):
        self.function = function
        self.max_hits = max_hits
        self.timeout = timeout
        self.cache = {}

    def __call__(self, *args):
        # Here the code returning the correct thing.


@Cache
def double(x):
    return x * 2

@Cache(max_hits=100, timeout=50)
def double(x):
    return x * 2
Run Code Online (Sandbox Code Playgroud)

第二个带有参数的装饰器覆盖默认的(max_hits=10, timeout=5在我的__init__函数中),不起作用,我得到了异常TypeError: __init__() takes at least 2 arguments (3 given).我尝试了许多解决方案,并阅读了有关它的文章,但在这里我仍然无法使它工作.

有什么想法解决这个问题?谢谢!

lun*_*chs 16

@Cache(max_hits=100, timeout=50)电话__init__(max_hits=100, timeout=50),所以你不满意这个function论点.

您可以通过检测函数是否存在的包装器方法实现装饰器.如果找到一个函数,它可以返回Cache对象.否则,它可以返回一个将用作装饰器的包装函数.

class _Cache(object):
    def __init__(self, function, max_hits=10, timeout=5):
        self.function = function
        self.max_hits = max_hits
        self.timeout = timeout
        self.cache = {}

    def __call__(self, *args):
        # Here the code returning the correct thing.

# wrap _Cache to allow for deferred calling
def Cache(function=None, max_hits=10, timeout=5):
    if function:
        return _Cache(function)
    else:
        def wrapper(function):
            return _Cache(function, max_hits, timeout)

        return wrapper

@Cache
def double(x):
    return x * 2

@Cache(max_hits=100, timeout=50)
def double(x):
    return x * 2
Run Code Online (Sandbox Code Playgroud)

  • 如果开发人员使用位置而不是关键字参数调用`Cache`(例如`@Cache(100,50)`),那么`function`将被赋值为100,而`max_hits`将被赋予错误.该函数被调用.这可以被认为是令人惊讶的行为,因为大多数人都期望统一的位置和关键字语义. (4认同)

unu*_*tbu 15

@Cache
def double(...): 
   ...
Run Code Online (Sandbox Code Playgroud)

相当于

def double(...):
   ...
double=Cache(double)
Run Code Online (Sandbox Code Playgroud)

@Cache(max_hits=100, timeout=50)
def double(...):
   ...
Run Code Online (Sandbox Code Playgroud)

相当于

def double(...):
    ...
double = Cache(max_hits=100, timeout=50)(double)
Run Code Online (Sandbox Code Playgroud)

Cache(max_hits=100, timeout=50)(double)具有非常不同的语义Cache(double).

尝试Cache处理这两个用例是不明智的.

您可以改为使用可以使用可选max_hitstimeout参数的装饰器工厂,并返回一个装饰器:

class Cache(object):
    def __init__(self, function, max_hits=10, timeout=5):
        self.function = function
        self.max_hits = max_hits
        self.timeout = timeout
        self.cache = {}

    def __call__(self, *args):
        # Here the code returning the correct thing.

def cache_hits(max_hits=10, timeout=5):
    def _cache(function):
        return Cache(function,max_hits,timeout)
    return _cache

@cache_hits()
def double(x):
    return x * 2

@cache_hits(max_hits=100, timeout=50)
def double(x):
    return x * 2
Run Code Online (Sandbox Code Playgroud)

PS.如果该类Cache除了__init__和之外没有其他方法__call__,您可以移动_cache函数内的所有代码并Cache完全消除.

  • @lunixbochs:将“cache_hits”(nee“cache”)与“cache_hits()”混淆的开发人员很可能将任何函数对象与函数调用混淆,或者将生成器与迭代器混淆。即使是经验丰富的 Python 程序员也应该习惯于关注其中的差异。 (2认同)

Ale*_*lig 11

我宁愿将包装器包含在类的__call__方法中:

更新: 此方法已在 python 3.6 中进行了测试,因此我不确定更高或更早的版本。

class Cache:
    def __init__(self, max_hits=10, timeout=5):
        # Remove function from here and add it to the __call__
        self.max_hits = max_hits
        self.timeout = timeout
        self.cache = {}

    def __call__(self, function):
        def wrapper(*args):
            value = function(*args)
            # saving to cache codes
            return value
        return wrapper

@Cache()
def double(x):
    return x * 2

@Cache(max_hits=100, timeout=50)
def double(x):
    return x * 2
Run Code Online (Sandbox Code Playgroud)