Python:将可选参数装饰器作为类实现

Posted

技术标签:

【中文标题】Python:将可选参数装饰器作为类实现【英文标题】:Python: Implementation of optional argument decorator as class 【发布时间】:2020-07-11 18:40:18 【问题描述】:

在阅读了出色的 Primer on Python Decorators 之后,我想到了将文章中的一些花哨(高级)装饰器作为练习来实现。

例如带有参数的装饰器示例

def repeat(num_times):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat
    return decorator_repeat

可以实现为这样的类

class Repeat:
    def __init__(self, times):
        self.times = times

    def __call__(self, fn):
        def _wrapper(*args, **kwargs):
            for _ in range(self.times):
                result = fn(*args, **kwargs)
            return result
        return _wrapper

但是我似乎无法为optional argument decorator example 找到类解决方案:

def repeat(_func=None, *, num_times=2):
    def decorator_repeat(func):
        @functools.wraps(func)
        def wrapper_repeat(*args, **kwargs):
            for _ in range(num_times):
                value = func(*args, **kwargs)
            return value
        return wrapper_repeat

    if _func is None:
        return decorator_repeat
    else:
        return decorator_repeat(_func)

只有我一个人,还是那个相当邪恶的人? XD 希望看到解决方案!

【问题讨论】:

【参考方案1】:

刚刚遇到这个老问题,又试了一次。

我认为这是一个相当有趣的(递归)解决方案:

class Repeat:
    
    def __init__(self, fn=None, *, times=2):
        self._fn = fn
        self._times = times

    def _fn_proxy(self, fn):
        self._fn = fn
        return self

    def __call__(self, *args, **kwargs):
        if self._fn:
            for _ in range(self._times):
                result = self._fn(*args, **kwargs)
            return result
        # assertion: if not self._fn, then args[0] must be the decorated function object
        return self._fn_proxy(args[0])

@Repeat
def fun(x,y):
    print(f"x and y and fun!")

@Repeat(times=4)
def more_fun(x,y):
    print(f"x and y and even more fun!")

fun(1,2)
print()
more_fun(3,4)

【讨论】:

【参考方案2】:

您可以重写__new__ 方法以实现相同的行为:

def __new__(cls, _func=None, *, times=2):
    obj = super().__new__(cls)
    obj.__init__(times)
    if _func is None:
        return obj
    else:
        return obj(_func)

这样两个:

@Repeat
def a():
    print('hi')

和:

@Repeat(times=2)
def a():
    print('hi')

输出:

hi
hi

【讨论】:

以上是关于Python:将可选参数装饰器作为类实现的主要内容,如果未能解决你的问题,请参考以下文章

python进阶之装饰器之2.定义一个可接受参数的装饰器如何定义一个属性可由用户修改的装饰器定义一个能接受可选参数的装饰器

python笔记第九天 装饰器

如何使用可选参数构建装饰器? [复制]

text 接收可选参数的装饰器

getopt 不将可选参数解析为参数

是否有必要在将可选参数传递给另一个可选参数之前检查它?