3.7 Python3 装饰器

装饰器(Decorator)是Python中一种强大的语法特性,它允许在不修改原函数代码的情况下,为函数添加额外的功能。


目录

1. 装饰器定义

2. 装饰器基本语法

3. 使用装饰器

3.1 简单装饰器示例

3.2 保留原函数信息

4. 带参数的装饰器

5. 类装饰器

5.1 函数形式的类装饰器

5.2 类形式的类装饰器

6. 内置装饰器

6.1 @property

6.2 @classmethod 和 @staticmethod

7. 多个装饰器的堆叠

8. 装饰器的应用

8.1 计时装饰器

8.2 缓存装饰器

8.3 权限检查装饰器

8.4 日志记录装饰器


1. 装饰器定义

装饰器本质上是一个可调用的Python对象(通常是函数或类),它接受一个函数作为输入并返回一个新的函数。装饰器的主要目的是在不改变原函数定义的情况下,扩展函数的行为。

装饰器的语法使用 @decorator_name 来应用在函数或方法上。

Python 还提供了一些内置的装饰器,比如 @staticmethod 和 @classmethod,用于定义静态方法和类方法。

装饰器的应用场景:

  • 日志记录: 装饰器可用于记录函数的调用信息、参数和返回值。
  • 性能分析: 可以使用装饰器来测量函数的执行时间。
  • 权限控制: 装饰器可用于限制对某些函数的访问权限。
  • 缓存: 装饰器可用于实现函数结果的缓存,以提高性能。

2. 装饰器基本语法

Python 装饰允许在不修改原有函数代码的基础上,动态地增加或修改函数的功能,装饰器本质上是一个接收函数作为输入并返回一个新的包装过后的函数的对象。

基本装饰器语法使用@符号:

@decorator
def function():
    pass

#这等同于:

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

3. 使用装饰器

3.1 简单装饰器示例

def simple_decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

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

say_hello()


这段代码演示了 Python 中装饰器的基本用法。装饰器是一种在不修改原函数代码的情况下,为函数添加额外功能的方式。

1. 定义装饰器函数 simple_decorator:

  • simple_decorator 是一个装饰器函数,它接收一个函数 func 作为参数

  • 内部定义了 wrapper 函数,它会在调用原函数前后打印信息

  • 最后返回 wrapper 函数

2. 应用装饰器:

  • @simple_decorator 语法是将 simple_decorator 装饰器应用到 say_hello 函数上

  • 这等同于: say_hello = simple_decorator(say_hello)

3. 调用被装饰的函数:

  • 实际调用的是 wrapper 函数

4. 执行流程:

  1. 当 Python 解释器遇到 @simple_decorator 时,它会立即装饰下面的函数

  2. say_hello 函数被传递给 simple_decorator 作为参数

  3. simple_decorator 返回 wrapper 函数,替换原来的 say_hello

  4. 当调用 say_hello() 时,实际上调用的是 wrapper() 函数

  5. wrapper() 执行其内部代码,包括调用原始 say_hello 函数

  

3.2 保留原函数信息

使用functools.wraps保留原函数的元信息:

from functools import wraps


def preserving_decorator(func):
    @wraps(func)  # 使用 wraps 保留 func 的元信息
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)

    return wrapper


@preserving_decorator
def greet(name):
    """Display a greeting message"""
    print(f"Hello, {name}!")


print(greet.__name__)  # 输出: greet
print(greet.__doc__)  # 输出: Display a greeting message
print("---------")
print(greet("KKK"))

这段代码演示了如何使用 functools.wraps 来保留被装饰函数的元信息(如函数名、文档字符串等)。

1. 导入 wraps 工具:

from functools import wraps
  • wraps 是一个装饰器工厂函数,用于保留被装饰函数的元信息

2. 定义装饰器函数 preserving_decorator:

def preserving_decorator(func):
    @wraps(func)  # 使用 wraps 保留 func 的元信息
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper
  • 装饰器内部使用 @wraps(func) 装饰 wrapper 函数

  • *args, **kwargs 使装饰器可以接受任意参数

  • 打印被调用函数名后执行原函数

3. 应用装饰器:

@preserving_decorator
def greet(name):
    """Display a greeting message"""
    print(f"Hello, {name}!")

4. 验证元信息保留:

print(greet.__name__)  # 输出: greet
print(greet.__doc__)   # 输出: Display a greeting message

5. 关键点说明:

  1. @wraps(func) 的作用:

    • 将原函数 (func) 的元信息(__name____doc____module__ 等)复制到包装函数 (wrapper) 中

    • 如果不使用 wrapsgreet.__name__ 会返回 wrappergreet.__doc__ 会返回 None

  2. 带参数的装饰器:

    • wrapper(*args, **kwargs) 使装饰器能够处理任何参数的函数

    • return func(*args, **kwargs) 确保原函数的返回值被正确传递

  3. 实际调用流程:
    • 先执行 wrapper("Alice")

    • 打印 "Calling greet"

    • 执行原 greet("Alice") 函数

    • 返回原函数的返回值(本例中为 None)

        4. 对比示例:

如果不使用 @wraps:

def bad_decorator(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@bad_decorator
def greet(name):
    """Display a greeting message"""
    print(f"Hello, {name}!")

print(greet.__name__)  # 输出: wrapper
print(greet.__doc__)   # 输出: None


4. 带参数的装饰器

装饰器本身也可以接受参数:

from functools import wraps

def repeat(num_times):  # 外层函数接收装饰器参数
    def decorator(func):  # 中间函数接收被装饰的函数
        @wraps(func)  # 保留原函数元信息
        def wrapper(*args, **kwargs):  # 内层包装函数
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator


@repeat(num_times=3)  # 使用装饰器并传入参数
def greet(name):
    print(f"Hello {name}")


greet("Alice")

这段代码演示了一个可以接受参数的装饰器 repeat,它会让被装饰的函数重复执行指定的次数。

代码解析

from functools import wraps

def repeat(num_times):  # 外层函数接收装饰器参数
    def decorator(func):  # 中间函数接收被装饰的函数
        @wraps(func)  # 保留原函数元信息
        def wrapper(*args, **kwargs):  # 内层包装函数
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

这是一个三层嵌套结构:

  1. 最外层 repeat(num_times) 接收装饰器参数

  2. 中间层 decorator(func) 接收被装饰的函数

  3. 最内层 wrapper(*args, **kwargs) 实现装饰逻辑

@repeat(num_times=3)  # 使用装饰器并传入参数
def greet(name):
    print(f"Hello {name}")

#这相当于:
greet = repeat(num_times=3)(greet)

执行流程

当调用 greet("Alice") 时:

  1. 首先执行 repeat(num_times=3),返回 decorator 函数

  2. 然后 decorator(greet) 被调用,返回 wrapper 函数

  3. 最后调用 wrapper("Alice"),它会:

    • 循环执行 greet("Alice") 3次

    • 返回最后一次执行的结果


5. 类装饰器

除了函数装饰器,Python 还支持类装饰器。

类装饰器(Class Decorator)是一种用于动态修改类行为的装饰器,它接收一个类作为参数,并返回一个新的类或修改后的类。

类装饰器可以用于:

  • 添加/修改类的方法或属性

  • 拦截实例化过程

  • 实现单例模式、日志记录、权限检查等功能

类装饰器有两种常见形式:

  • 函数形式的类装饰器(接收类作为参数,返回新类)

  • 类形式的类装饰器(实现 __call__ 方法,使其可调用)

5.1 函数形式的类装饰器

def class_decorator(cls):  # 类装饰器函数,接收一个类作为参数
    class Wrapped(cls):    # 创建一个继承自原类的新类
        def new_method(self):
            print("This is a new method added by decorator")
            
        def existing_method(self):
            print("Method is being modified by decorator")
            super().existing_method()  # 调用父类(原类)的方法
    
    return Wrapped  # 返回修改后的新类

@class_decorator  # 应用类装饰器
class MyClass:
    def existing_method(self):
        print("Original method")

obj = MyClass()
print("-----------")
obj.existing_method()  # 被装饰器修改的方法
print("-----------")
obj.new_method()  # 装饰器添加的新方法
print("-----------")
obj.existing_method()  # 被装饰器修改的方法
print("-----------")

关键点说明:

  1. 类装饰器原理

    • 接收一个类作为参数

    • 创建一个继承自原类的新类 Wrapped

    • 在新类中添加或修改方法

    • 返回修改后的新类

  2. 方法覆盖与扩展

    • new_method():完全新增的方法

    • existing_method():修改原有方法,通过 super() 保留原功能

  3. 继承关系

    Wrapped -> cls (原类) -> object

  4. 元信息保留

    • 类名仍然是 MyClass(因为 Wrapped 是内部类名)

    • 原类的其他属性和方法保持不变

    

5.2 类形式的类装饰器

class ClassDecorator:
    def __init__(self, func):               # 初始化方法,接收被装饰的函数
        self.func = func                    # 保存函数为实例属性
        
    def __call__(self, *args, **kwargs):    # 使实例可调用
        print("Before function call")
        result = self.func(*args, **kwargs) # 调用原函数
        print("After function call")
        return result                       # 返回原函数的结果

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

say_hello()

这段代码展示了如何使用类来实现装饰器功能。与函数装饰器不同,类装饰器利用了类的 __init__ 和 __call__ 方法来实现装饰逻辑。

执行流程:

  1. 当解释器遇到 @ClassDecorator 时:

    • 创建 ClassDecorator 实例,调用 __init__ 方法

    • 将被装饰函数 say_hello 传递给 func 参数保存

  2. 当调用 say_hello() 时:

    • 实际上是调用 ClassDecorator 实例的 __call__ 方法

    • 执行 __call__ 方法中的包装逻辑

    • 最终调用原始 say_hello 函数

关键点说明:

  1. 类装饰器工作原理

    • __init__:在装饰时调用,接收被装饰函数

    • __call__:在被装饰函数调用时执行,实现装饰逻辑

  2. 与函数装饰器的区别

    • 函数装饰器使用嵌套函数

    • 类装饰器使用类方法和实例状态

  3. 优势

    • 可以维护状态(通过实例属性)

    • 结构更清晰,适合复杂装饰逻辑

    • 可以通过继承扩展功能

扩展示例:

带参数的类装饰器:

class ParametrizedDecorator:
    def __init__(self, prefix):
        self.prefix = prefix
        
    def __call__(self, func):
        def wrapper(*args, **kwargs):
            print(f"{self.prefix} - Before call")
            result = func(*args, **kwargs)
            print(f"{self.prefix} - After call")
            return result
        return wrapper

@ParametrizedDecorator("DEBUG")
def say_hi():
    print("Hi!")

say_hi()


6. 内置装饰器

Python提供了一些有用的内置装饰器:

  1. @staticmethod: 将方法定义为静态方法,不需要实例化类即可调用。

  2. @classmethod: 将方法定义为类方法,第一个参数是类本身(通常命名为 cls)。

  3. @property: 将方法转换为属性,使其可以像属性一样访问。

6.1 @property

class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def radius(self):
        return self._radius
    
    @radius.setter
    def radius(self, value):
        if value >= 0:
            self._radius = value
        else:
            raise ValueError("Radius must be positive")

c = Circle(5)      # 初始化半径为5
print(c.radius)    # 通过property获取值 → 5
c.radius = 10      # 通过setter设置值
print(c.radius)    # 10
c.radius = -5      # 触发ValueError异常

关键组件说明

  1. @property 装饰器

    • 将方法转换为只读属性

    • 当访问 instance.radius 时自动调用此方法

  2. Setter 方法

    • 使用 @属性名.setter 装饰器定义

    • 当赋值 instance.radius = value 时自动调用

    • 可以添加验证逻辑

  3. 命名约定

    • _radius:单下划线表示受保护属性(约定上的私有)

    • 公共接口使用无下划线名称

设计优势

  1. 封装性

    • 隐藏内部实现细节(使用 _radius 存储实际值)

    • 对外提供统一的访问接口

  2. 数据验证

    • 确保半径不会设置为负数

    • 在赋值时立即捕获非法输入

  3. 兼容性

    • 可以随时修改内部实现而不影响外部代码

    • 例如未来可以改为存储直径而非半径

6.2 @classmethod 和 @staticmethod

class MyClass:
    class_var = "Class variable"
    
    def __init__(self, instance_var):
        self.instance_var = instance_var
    
    @classmethod
    def class_method(cls):
        print(f"Accessing {cls.class_var} from class method")
    
    @staticmethod
    def static_method():
        print("This is a static method")

MyClass.class_method()   # 不需要实例
MyClass.static_method()  # 不需要实例

关键概念说明

1. 类方法 (@classmethod)

  • 特点

    • 第一个参数是 cls (类对象本身)

    • 可以访问类变量和其他类方法

    • 不能访问实例变量(因为不需要实例就能调用)

2. 静态方法 (@staticmethod)

  • 特点

    • 不需要 self 或 cls 参数

    • 不能访问类变量或实例变量

    • 类似于普通函数,只是属于类的命名空间


7. 多个装饰器的堆叠

装饰器可以堆叠使用,执行顺序是从下往上:

def decorator1(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Decorator 1 - Before")
        result = func(*args, **kwargs)
        print("Decorator 1 - After")
        return result
    return wrapper

def decorator2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Decorator 2 - Before")
        result = func(*args, **kwargs)
        print("Decorator 2 - After")
        return result
    return wrapper

@decorator1
@decorator2
def my_function():
    print("Original function")

my_function()

装饰器的应用顺序是从下往上的,即:

  1. 先应用 @decorator2

  2. 然后应用 @decorator1

函数调用

当调用 my_function() 时,执行顺序如下:

  1. 执行 decorator1 的 wrapper 前半部分 - 打印 "Decorator 1 - Before"

  2. 执行 decorator2 的 wrapper 前半部分 - 打印 "Decorator 2 - Before"

  3. 执行原始函数 my_function - 打印 "Original function"

  4. 执行 decorator2 的 wrapper 后半部分 - 打印 "Decorator 2 - After"

  5. 执行 decorator1 的 wrapper 后半部分 - 打印 "Decorator 1 - After"

关键点

  1. 装饰器是从下往上应用的

  2. 执行顺序是"由外向内"再"由内向外"

  3. @wraps(func) 保留了原始函数的元信息

  4. 装饰器可以叠加使用,形成装饰器链

这种装饰器模式常用于添加日志、权限检查、性能测试等横切关注点功能。


8. 装饰器的应用

装饰器在实际开发中有广泛的应用:

8.1 计时装饰器

import time
from functools import wraps

def timer(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(2)

slow_function()

8.2 缓存装饰器

from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(30))  # 快速计算,因为有缓存

8.3 权限检查装饰器

from functools import wraps


def requires_admin(func):
    @wraps(func)
    def wrapper(user, *args, **kwargs):
        if user.is_admin:
            return func(user, *args, **kwargs)
        else:
            raise PermissionError("Admin privileges required")
    return wrapper

class User:
    def __init__(self, name, is_admin):
        self.name = name
        self.is_admin = is_admin

@requires_admin
def delete_database(user):
    print(f"Database deleted by {user.name}")

admin = User("Admin", True)
regular = User("Regular", False)

delete_database(admin)  # 正常工作
delete_database(regular)  # 抛出PermissionError

8.4 日志记录装饰器

from functools import wraps


def log_activity(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}")
        result = func(*args, **kwargs)
        print(f"{func.__name__} returned: {result}")
        return result
    return wrapper

@log_activity
def add_numbers(a, b):
    return a + b

add_numbers(3, 5)

装饰器是Python中非常强大的工具,合理使用可以使代码更加简洁、可读和可维护。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值