解析几何太难啦QAQ 2025-05-18 08:01 采纳率: 100%
浏览 5
已结题

有关装饰器的问题,希望各位Python大蛇解答

通过某种操作,让下面这样基于类的装饰器既可以不传参数调用,也可以传参数调用,实现灵活的接口。

from functools import update_wrapper
import time

class Delay():
    def __init__(self, func, *, times: int=1) -> None:
        self.func = func
        self.times = times
        update_wrapper(self, func)
    def __call__(self, *args, **kwargs):
        print(f'休息{self.times}秒先')
        time.sleep(self.times)
        return self.func(*args, **kwargs)

@Delay
def hi(name: str) -> None:
    print(f'你好呀,{name}!')
@Delay(times=2)
def hello(name: str) -> None:
    print(f'你好呀,{name}!')

本人现在只想到使用元类,这个问题还有其它解决方案吗?

  • 写回答

4条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-05-18 08:03
    关注

    让【宝码香车】来帮你解答,本回答参考DeepSeek编写,并整理提供。
    如果还有疑问可以点击头像关注私信或评论。如果答案让您满意,请采纳、点赞、关注,非常感谢!

    Python装饰器灵活调用方案

    要实现一个既可以带参数调用也可以不带参数调用的类装饰器,有几种常见方法,元类只是其中一种。下面我将介绍几种更简洁的实现方式:

    1. 使用__new__方法实现

    这是最常用的解决方案,通过重写__new__方法来处理不同调用情况:

    from functools import update_wrapper
    import time
    
    
    class Delay:
        def __new__(cls, func=None, *, times=1):
            if func is None:
                return lambda f: Delay(f, times=times)
            instance = super().__new__(cls)
            instance.__init__(func, times=times)
            return instance
            
        def __init__(self, func, *, times=1):
            self.func = func
            self.times = times
            update_wrapper(self, func)
            
        def __call__(self, *args, **kwargs):
            print(f'休息{self.times}秒先')
            time.sleep(self.times)
            return self.func(*args, **kwargs)
    

    2. 使用函数包装器实现

    这种方法将类装饰器包装在一个函数中:

    from functools import update_wrapper
    import time
    
    
    def Delay(func=None, *, times=1):
        if func is None:
            return lambda f: Delay(f, times=times)
        
        class _Delay:
            def __init__(self, func, times):
                self.func = func
                self.times = times
                update_wrapper(self, func)
                
            def __call__(self, *args, **kwargs):
                print(f'休息{self.times}秒先')
                time.sleep(self.times)
                return self.func(*args, **kwargs)
                
        return _Delay(func, times)
    

    3. 使用类方法实现

    这种方法利用类方法作为工厂函数:

    from functools import update_wrapper
    import time
    
    
    class Delay:
        def __init__(self, func, *, times=1):
            self.func = func
            self.times = times
            update_wrapper(self, func)
            
        def __call__(self, *args, **kwargs):
            print(f'休息{self.times}秒先')
            time.sleep(self.times)
            return self.func(*args, **kwargs)
            
        @classmethod
        def with_times(cls, times):
            return lambda func: cls(func, times=times)
    

    使用方式:

    @Delay
    def hi(name: str) -> None:
        print(f'你好呀,{name}!')
    
    
    @Delay.with_times(2)
    def hello(name: str) -> None:
        print(f'你好呀,{name}!')
    

    推荐方案

    第一种使用__new__的方法是最简洁和Pythonic的,它不需要额外的包装函数或类方法,保持了装饰器的直观性。这也是Python标准库中许多装饰器(如@property)采用的实现方式。

    装饰器调用流程:

    调用装饰器

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 5月26日
  • 已采纳回答 5月18日
  • 创建了问题 5月18日