【问题标题】:Python: Implementation of optional argument decorator as classPython:将可选参数装饰器作为类实现
【发布时间】: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 希望看到解决方案!

【问题讨论】:

    标签: python oop decorator


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-11-21
        • 2011-08-26
        • 2021-07-16
        • 2012-02-20
        • 1970-01-01
        • 1970-01-01
        • 2014-03-24
        相关资源
        最近更新 更多