装饰器介绍

#函数装饰器是一个接收函数作为参数并返回一个新函数的高阶函数。它可以在不修改原函数代码的情况下,为函数添加额外功能。
"""
函数装饰器是Python中一个非常强大和有用的特性,它允许在不修改原函数代码的情况下,给函数增加新的功能。装饰器本质上是一个函数,它接受一个函数作为参数,并返回一个新的函数。
下面我将从基本概念开始,逐步讲解如何创建和使用装饰器,包括带参数的装饰器和类装饰器。
1.基本装饰器
首先,我们来看一个最简单的装饰器例子,它会在函数执行前后打印一些信息。

2.使用@语法糖
在Python中,我们可以使用@符号来应用装饰器,这是一种更简洁的写法。

3.装饰器带参数
有时候我们需要装饰器本身可以接受参数,这样我们可以根据参数的不同来调整装饰器的行为。

4.使用类作为装饰器
除了函数,类也可以被用来创建装饰器。类装饰器通常通过实现__call__方法来实现。

5.多个装饰器
一个函数可以同时使用多个装饰器,它们的应用顺序是从下往上(或从内到外)。

6.使用functools.wraps
在定义装饰器时,最好使用functools.wraps来保留原函数的元信息(如函数名、文档字符串等)。

"""
# @decorator()
# def function():
#     pass

# # 等价于

# def function():
#     pass
# function = decorator(function)

# --------------------------------------------------------------------------
# 1.定义1个装饰器
def my_decorator(func):
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper  

# 2.使用装饰器
@my_decorator
def say_hello():
    print("Hello!")

# 3.调用函数
say_hello()

# --------------------------------------------------------------------------
# 装饰带参数的函数
# --------------------------------------------------------------------------
def timer(func):
    def wrapper(*args,**kwargs):
        import time
        start = time.time()
        result = func(*args,**kwargs)
        end = time.time()
        print(f"函数{func.__name__}执行耗时:{end-start:.4f}秒")
        return result
    return wrapper

@timer
def calculate_sum(n):
    return sum(range(n))

print(calculate_sum(10000))

# ------------------------------------------------------------------------
#带参数的装饰器
# ------------------------------------------------------------------------
"""
目标:我们想要一个可以指定重复次数的装饰器,比如重复执行某个函数多次。
首先,repeat 是一个装饰器工厂,它接收一个参数 times,然后返回一个装饰器(也就是返回一个函数,这个函数将用来装饰目标函数)。
decorator 是真正的装饰器,它接收一个函数 func 作为参数(就是我们要装饰的函数)。
在 decorator 内部,我们定义了一个包装函数 wrapper,它负责执行 times 次 func,并返回最后一次的结果。
最后,repeat 返回 decorator。
当我们使用 @repeat(times=3) 时,实际上发生了以下步骤:
调用 repeat(times=3),返回 decorator 函数。
然后,将这个返回的 decorator 应用到 greet 函数上,即 greet = decorator(greet)。
现在,greet 实际上已经变成了 wrapper 函数。当我们调用 greet("Alice") 时,实际上是在调用 wrapper("Alice"),而 wrapper 内部会循环3次调用原来的 greet 函数。
注意:这个装饰器有一个问题,它只返回最后一次调用的结果。如果被装饰的函数没有返回值,那么循环3次后,result 会是 None,然后返回 None。如果函数有返回值,那么前两次的返回值会被覆盖,只保留最后一次的返回值。

装饰器工厂:repeat(times) 是一个函数,接收参数,返回真正的装饰器 decorator

装饰器:decorator(func) 接收要装饰的函数,返回包装函数 wrapper

包装函数:wrapper(*args, **kwargs) 实际被调用的函数,它包含额外的逻辑(重复执行)

闭包作用:wrapper 可以访问外层函数的变量 times 和 func,即使外层函数已经执行完毕

"""
def repeat(times):
    """装饰器工厂:返回一个装饰器"""
    def decorator(func):
        def wrapper(*args,**kwargs):
            for _ in range(times):
                result = func(*args,**kwargs)
            #     仅返回最后一次调用的结果
            return result
        return wrapper
    return decorator

@repeat(times=3)
def greet(name):
    print(f"Hello,{name}")
    return 0

greet("Alice")
# -----------------------------------------------------------------------
# 改进
# -----------------------------------------------------------------------
def repeat(times):
    def decorator(func):
        def wrapper(*args,**kwargs):
            results = []
            for _ in range(times):
                results.append(func(*args,**kwargs))
            return results
        return wrapper
    return decorator









# ------------------------------------------------------------------------
# 类装饰器
# ------------------------------------------------------------------------
class CountCalls:
    def __init__(self,func):
        self.cfunc = func
        self.ccalls = 0

    def __call__(self, *args, **kwargs):
        self.ccalls += 1
        print(f"第{self.ccalls}次调用{self.cfunc.__name__}")
        return self.cfunc(*args,**kwargs)

@CountCalls
def say_hello():
    print("Hello!")

say_hello()
say_hello()
say_hello()

# ---------------------------------------------------------------------
# 多个装饰器
# ---------------------------------------------------------------------
def decorator1(func):
    def wrapper():
        print("装饰器1-前")
        func()
        print("装饰器1-后")
    return wrapper

def decorator2(func):
    def wrapper():
        print("装饰器2 - 前")
        func()
        print("装饰器2 - 后")
    return wrapper

@decorator1
@decorator2
def my_function():
    print("原函数")

my_function()
# 输出:
# 装饰器1 - 前
# 装饰器2 - 前
# 原函数
# 装饰器2 - 后
# 装饰器1 - 后

# ---------------------------------------------------
# 类装饰器
# ---------------------------------------------------
"""
类装饰器是一个接收类作为参数并返回修改后的类的装饰器。它使用类来实现装饰器功能,通常通过实现__init__ 和__call__方法。

"""
class ClassDecorator:
    def __init__(self,cls):
        """初始化,接收被装饰的类"""
        self.cls = cls

    def __call__(self, *args, **kwargs):
        """当装饰后的类被实例化时调用"""
        return isinstance

@ClassDecorator
class MyClass:
    pass

# 等价于
MyClass = ClassDecorator(MyClass)

# 示例1:添加类属性/方法
class AddAttributes:
    """添加额外属性和方法的装饰器"""
    def __init__(self,cls):
        # 参数	指向      使用场景	    装饰器
        # self	类的实例	实例方法	无或@staticmethod
        # cls	类本身	类方法	@classmethod
        self.cls = cls
        # 添加类属性
        cls.version = "1.0.0"
        cls.author = "system"
        # 添加类方法
        """
        lambda是创建匿名函数的快捷方式
        
        def add(a,b):
            return a+b
        
        等价于
        add = lambda a,b : a + b
        
        对于实例方法,需要self参数:
        normal_method = lambda self :self.name.upper()
        ----------------------------------------------
        class Person2:
            pass
        # 添加方法时必须有self参数
        Person2.say_hello = lambda self : f"Hello ,I am {self.name}"
        
        p2 = Person2()
        p2.name = "Mayer"
        # Hello ,I am Mayer
        print(p2.say_hello())    
        --------------------------------------------
        class Myclass:
            # 类属性
            version = "2.0.0"
        # cls 指向Myclass 这个类对象
        # Myclass
        print(Myclass.__name__)
        # 2.0.0
        print(Myclass.version)
        # 在方法中访问
        Myclass.get_info = lambda self:f"{self.__class__.__name__}v{self.__class__.version}"
        
        obj = Myclass()
        # MyClass v2.0.0
        print(obj.get_info())





        """
        cls.get_info  = lambda self :f"{cls.__name__}v{cls.version}"


    def __call__(self, *args, **kwargs):
        return self.cls(*args,**kwargs)

  

posted @ 2026-05-30 10:27  此心安处2022  阅读(2)  评论(0)    收藏  举报