导航菜单

  • 1.VSCode开发
  • 2.什么是Python?
  • 3.请详细解释Python代码的执行过程
  • 4.请详细解释解释型语言与编译型语言的主要区别
  • 5.你知道哪些Python的编码规范?
  • 6.数据类型
  • 7.Python中如何声明多个变量并赋值
  • 8.Python有哪些内置数据结构
  • 9.!=和is not运算符有什么区别?
  • 10.进制
  • 11.编码
  • 12.print
  • 13.Python中break、continue、pass有什么作用?
  • 14.namedtuple有什么作用?
  • 15.Python的range函数如何运用?
  • 16.Python中join()和split()函数有什么区别?
  • 17.Python中如何将字符串转换为小写?
  • 18.Python中如何删除字符串中的前置空格?
  • 19.Python中如何使用索引反转字符串
  • 20.什么是Python的成员运算符?
  • 21.请详细说明Python中逻辑运算符(`and`、`or`、`not`)
  • 22.什么是Python的关系运算符?
  • 23.什么是Python的赋值和算术运算符?请详细说明赋值运算符、算术运算符的种类、使用方法、优先级规则。
  • 24.请详细解释Python中整数除法、取模运算和幂运算三个运算符。
  • 25.如何在Python中表示和转换不同进制的数字
  • 26.什么是Python的位运算符?
  • 27.请详细说明Python中三元表达式(Ternary Expression)的工作原理
  • 28.Python中如何实现switch语句?
  • 29.什么是Python的负索引?
  • 30.Python中如何实现字符串替换操作?
  • 31.Python中append、insert和extend有什么区别?
  • 32.请详细说明Python中`enumerate()`函数的作用
  • 33.Python中remove、del和pop有什么区别?
  • 34.Python中如何更改列表元素的数据类型?
  • 35.请详细说明Python中列表(list)和元组(tuple)的区别
  • 36.什么是Python元组的解封装?
  • 37.详细说明Python字典
  • 38.Python中KeyError、TypeError和ValueError有什么区别?
  • 39.请详细解释Python中`read()`、`readline()`和`readlines()`三种文件读取方法
  • 40.Python中iterable、iterator和generator的区别与联系
  • 41.Python中如何读取大文件?
  • 42.请详细解释Python中浅拷贝(shallow copy)和深拷贝(deep copy)的区别
  • 43.什么是Python的Lambda函数?
  • 44.Python中的reduce函数有什么作用?
  • 45.Python的zip函数有什么作用?
  • 46.请详细解释Python中`any()`和`all()`内置函数的作用
  • 47.为什么Python中没有函数重载?
  • 48.请介绍Python中变量的作用域(Scope)?
  • 49.什么是Python的闭包
  • 50.请详细说明Python中的内存管理机制
  • 51.请详细说明Python程序退出时内存的释放情况
  • 52.Python中是否有严格意义上的main函数?
  • 53.什么是Python的pickling和unpickling?
  • 54.什么是Python的猴子补丁(monkey patching)?
  • 55.什么是Python的鸭子类型(Duck Typing)
  • 56.什么是Python中的面向对象编程
  • 57.Python是否支持多重继承
  • 58.请详细说明Python3中装饰器的用法
  • 59.什么是Python中的模块和包?
  • 60.你使用过哪些Python标准库模块?
  • 61.你知道哪些Python魔术方法
  • 62.讲一下Python多线程、多进程和线程池
  • 63.如何分析Python代码的执行性能?
  • 64.pip
  • 65.pip-m
  • 67.uv
  • utf8
  • ast
  • dis
  • 尾递归
  • MethodType
  • 请详细说明Python3中装饰器的用法
  • 1. 装饰器的基本概念
  • 2. 基本装饰器语法
    • 2.1 简单装饰器示例
    • 2.2 带参数的装饰器
  • 3. 类装饰器
  • 4. 装饰类的装饰器
  • 5. 使用functools.wraps保持元数据
  • 6. 实际应用场景示例
    • 6.1 性能测试装饰器
    • 6.2 缓存装饰器
    • 6.3 日志记录装饰器
    • 6.4 权限验证装饰器
    • 6.5 事务处理装饰器
    • 6.6 参数验证装饰器
    • 6.7 重试机制装饰器
    • 6.8 监控告警装饰器
  • 7. 注意事项
  • 8. inspect
    • 8.1 inspect.signature(func) - 获取函数签名
    • 8.2 签名的结构
    • 8.3 sig.bind(*args, **kwargs) - 参数绑定
    • 8.4 BoundArguments 对象
    • 8.5. bound_args.apply_defaults() - 应用默认值
    • 8.6 annotations
      • 8.6.1 基本说明
      • 8.6.2 示例
      • 8.6.3 在装饰器中的应用
    • 8.7 自动参数检查
  • 9.参考回答
    • 9.1 什么是装饰器(开门见山)
    • 9.2 核心工作原理(展现理解深度)
    • 9.3 主要类型(展示知识广度)
    • 9.4 实际应用场景(体现实战经验)
    • 9.5 重要注意事项(展现专业素养)
    • 9.6 加分项(如果时间允许)
    • 9.7 回答技巧

请详细说明Python3中装饰器的用法 #

包括基本概念、语法、应用场景以及高级用法。

1. 装饰器的基本概念 #

装饰器是Python中的一种设计模式,它允许我们在不修改原函数代码的情况下,动态地增强或修改函数、方法或类的功能。装饰器本质上是一个接受函数作为参数并返回新函数的高阶函数。

2. 基本装饰器语法 #

装饰器的基本语法主要包括三部分:

  1. 定义装饰器函数
    装饰器通常是一个函数,参数为另一个函数对象,并返回一个新的函数。

  2. 在目标函数上应用装饰器
    可通过@装饰器名语法糖,直接将装饰器作用于目标函数。

  3. 返回包装后的新函数
    装饰器用内部函数对原函数进行包装,在内部可以加入自定义功能。

基本结构示例:

def decorator_name(func):
    def wrapper(*args, **kwargs):
        # 在调用原始函数前可加入自定义操作
        print("开始执行")
        result = func(*args, **kwargs)
        # 在调用原始函数后可加入自定义操作
        print("执行结束")
        return result
    return wrapper

@decorator_name  # 等效于 func = decorator_name(func)
def func():
    print("这是原始函数内容")
func()    
function decoratorName(func) {
    return function(...args) {
        // 在调用原始函数前可加入自定义操作
        console.log("开始执行");
        const result = func(...args);
        // 在调用原始函数后可加入自定义操作
        console.log("执行结束");
        return result;
    }
}

// 等效于 func = decoratorName(func)
function func() {
    console.log("这是原始函数内容");
}

const decoratedFunc = decoratorName(func);
decoratedFunc();

这样,调用 func() 时,实际执行的是装饰器包装后的新功能。

2.1 简单装饰器示例 #

# 定义一个简单的装饰器函数
def my_decorator(func):
    # 定义内部包装函数
    def wrapper():
        # 在函数调用前执行的操作
        print("函数调用前:正在准备执行...")
        # 调用原始函数
        func()
        # 在函数调用后执行的操作
        print("函数调用后:执行完成!")
    # 返回包装函数
    return wrapper

# 使用@语法糖应用装饰器
@my_decorator
def say_hello():
    # 原始函数功能
    print("Hello, World!")

# 调用被装饰的函数
say_hello()

输出结果:

函数调用前:正在准备执行...
Hello, World!
函数调用后:执行完成!

2.2 带参数的装饰器 #

# 定义装饰器工厂函数,接受参数
def repeat(num_times):
    # 返回实际的装饰器函数
    def decorator(func):
        # 定义包装函数,处理参数传递
        def wrapper(*args, **kwargs):
            # 根据指定次数重复执行函数
            for i in range(num_times):
                print(f"第 {i+1} 次执行:")
                # 调用原始函数并传递所有参数
                result = func(*args, **kwargs)
            # 返回最后一次执行的结果
            return result
        # 返回包装函数
        return wrapper
    # 返回装饰器函数
    return decorator

# 使用带参数的装饰器
@repeat(3)
def greet(name):
    # 问候函数
    print(f"Hello {name}!")
    # 返回问候信息
    return f"Greeting for {name}"

# 调用被装饰的函数
result = greet("Python")
print(f"最终结果:{result}")

输出结果:

第 1 次执行:
Hello Python!
第 2 次执行:
Hello Python!
第 3 次执行:
Hello Python!
最终结果:Greeting for Python

3. 类装饰器 #

类装饰器就是用类来实现装饰器功能,通常通过定义__call__方法,使其实例像函数一样可以被调用,从而在方法执行前后添加自定义逻辑。类装饰器的优点在于可以保存状态(比如计数、缓存)和更强的扩展性。

核心原理:

  • 类装饰器接收一个函数(或类),返回一个新的可调用对象(通常是其本身的实例)。
  • 通过实现__init__和__call__方法,自定义装饰逻辑。

基本用法举例:

# 定义一个类装饰器Logger
class Logger:
    # 初始化方法,接收被装饰的函数
    def __init__(self, func):
        # 保存被装饰的函数到实例属性
        self.func = func

    # 定义__call__方法,使实例可以像函数一样被调用
    def __call__(self, *args, **kwargs):
        # 打印函数开始执行的信息
        print(f"开始执行:{self.func.__name__}")
        # 调用被装饰的函数并保存结果
        result = self.func(*args, **kwargs)
        # 打印函数结束执行的信息
        print(f"结束执行:{self.func.__name__}")
        # 返回函数的返回值
        return result

# 使用类装饰器Logger来装饰函数say_hello
@Logger
# 定义一个打印问候语的函数
def say_hello(name):
    # 打印问候信息
    print(f"Hello, {name}!")

# 调用被装饰的say_hello函数
say_hello("Python")

输出结果:

开始执行:say_hello
Hello, Python!
结束执行:say_hello

4. 装饰类的装饰器 #

装饰类的装饰器(Class Decorator for Classes),即“修饰一个类本身的装饰器”,用于动态地为类增加属性、方法,甚至修改类的行为。这类装饰器的参数是“类”,返回值通常是修改或扩充后的类本身。常见用途包括:为所有实例增加某种通用功能、注入日志、权限检查、单例模式实现等。

核心原理:

  • 类装饰器的参数是类对象本身(不是实例、不是函数)。
  • 可以直接操作类本身,比如动态添加属性或方法、修改父类、返回经过更改的新类。

基本用法举例:

# 定义一个单例模式的类装饰器
def singleton(cls):
    # 用于保存类的实例
    _instance = {}
    # 定义返回实例的函数
    def get_instance(*args, **kwargs):
        # 如果实例不存在,则创建一个新的实例
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)
        # 返回实例
        return _instance[cls]
    # 返回获取实例的函数
    return get_instance

# 使用singleton装饰器修饰MyDatabase类
@singleton
class MyDatabase:
    # 初始化方法
    def __init__(self):
        # 输出初始化信息
        print("初始化数据库连接")

# 创建第一个数据库实例
db1 = MyDatabase()
# 创建第二个数据库实例
db2 = MyDatabase()
# 判断db1和db2是否为同一对象,并输出结果
print(db1 is db2)  # True

输出结果:

初始化数据库连接
True

此外,也可以用类装饰器为类注入通用功能,例如动态添加新方法:

# 定义一个装饰器函数 add_say_hi,参数为类对象 cls
def add_say_hi(cls):
    # 在装饰器内部定义一个实例方法 say_hi,参数为实例 self
    def say_hi(self):
        # 打印出带有类名的问候语
        print(f"Hi,我是{self.__class__.__name__}")
    # 将 say_hi 方法赋值给类 cls,使其成为该类的实例方法
    cls.say_hi = say_hi
    # 返回被修改过的类 cls
    return cls

# 使用 add_say_hi 装饰 People 类,给类动态添加 say_hi 方法
@add_say_hi
class People:
    # 定义一个空类 People
    pass

# 实例化 People 类,创建对象 p
p = People()
# 调用 p 对象的 say_hi 方法,输出 Hi,我是People
p.say_hi()  # Hi,我是People

注意事项:

  • 类装饰器不要误用于实例;本质是“接收一个类,返回一个类或可调用对象”。
  • 和函数装饰器一样,可链式叠加,装饰多个功能。

5. 使用functools.wraps保持元数据 #

在使用装饰器时,通常会希望被装饰函数的元信息(如函数名、文档字符串、注释等)能够保留,否则被装饰后函数的__name__、__doc__等属性会丢失。为了解决这个问题,Python标准库提供了functools.wraps装饰器。它的作用是将原函数的重要元数据复制到包装函数wrapper上。

用法说明:

  • 在自定义装饰器的内部包装函数外层,加上@wraps(func),这样包装函数就能“伪装”成原来的函数。
  • 这对于调试、文档生成、元编程等场景尤其重要。
# 导入functools模块中的wraps装饰器
from functools import wraps

# 定义保持元数据的装饰器
def my_decorator(func):
    # 使用wraps装饰器保持原函数的元数据
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 在函数执行前打印信息
        print("函数执行前:开始处理...")
        # 调用原始函数并获取返回值
        result = func(*args, **kwargs)
        # 在函数执行后打印信息
        print("函数执行后:处理完成!")
        # 返回原始函数的返回值
        return result
    # 返回包装函数
    return wrapper

# 使用装饰器
@my_decorator
def calculate_sum(a, b):
    """
    计算两个数的和

    Args:
        a (int): 第一个数
        b (int): 第二个数

    Returns:
        int: 两数之和
    """
    # 计算并返回两数之和
    return a + b

# 调用被装饰的函数
result = calculate_sum(5, 3)
print(f"计算结果:{result}")

# 验证元数据是否保持
print(f"函数名:{calculate_sum.__name__}")
print(f"函数文档:{calculate_sum.__doc__}")
print(f"函数模块:{calculate_sum.__module__}")

输出结果:

函数执行前:开始处理...
函数执行后:处理完成!
计算结果:8
函数名:calculate_sum
函数文档:
计算两个数的和

Args:
    a (int): 第一个数
    b (int): 第二个数

Returns:
    int: 两数之和

函数模块:__main__

6. 实际应用场景示例 #

6.1 性能测试装饰器 #

# 导入时间模块
import time
# 从functools模块导入wraps函数
from functools import wraps

# 定义性能测试装饰器函数,接收一个参数func
def performance_test(func):
    # 使用wraps装饰,使包装函数保留原函数信息
    @wraps(func)
    # 定义包装函数,接受任意位置和关键字参数
    def wrapper(*args, **kwargs):
        # 记录函数开始执行的时间
        start_time = time.time()
        # 调用原始函数并获取其返回值
        result = func(*args, **kwargs)
        # 记录函数结束执行的时间
        end_time = time.time()
        # 计算函数的执行时长
        execution_time = end_time - start_time
        # 打印函数名称及其执行时间
        print(f"函数 {func.__name__} 执行时间:{execution_time:.4f} 秒")
        # 返回原始函数的执行结果
        return result
    # 返回作为装饰器的包装函数
    return wrapper

# 使用performance_test装饰器装饰slow_function函数
@performance_test
# 定义一个模拟耗时操作的函数,接受一个参数n
def slow_function(n):
    # 初始化total为0
    total = 0
    # 遍历从0到n-1的所有整数
    for i in range(n):
        # 每次循环将i累加到total中
        total += i
    # 返回累加结果
    return total

# 调用slow_function函数,并将结果赋值给result变量
result = slow_function(1000000)
# 打印slow_function函数的计算结果
print(f"计算结果:{result}")

6.2 缓存装饰器 #

# 导入 wraps,用于保持原函数的元数据
from functools import wraps

# 定义一个全局的缓存字典
cache = {}

# 定义一个缓存装饰器函数
def cache_result(func):
    # 使用 @wraps 保证被装饰函数的签名信息不变
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 基于输入参数生成唯一的缓存键
        cache_key = str(args) + str(sorted(kwargs.items()))
        # 如果缓存中已有结果,则直接返回缓存内容
        if cache_key in cache:
            print(f"从缓存获取结果:{cache_key}")
            return cache[cache_key]
        # 如果缓存中没有结果,则计算函数结果
        print(f"计算新结果:{cache_key}")
        result = func(*args, **kwargs)
        # 将新计算的结果存入缓存
        cache[cache_key] = result
        # 返回计算得到的结果
        return result
    # 返回包装后的新函数
    return wrapper

# 使用缓存装饰器装饰 fibonacci 函数
@cache_result
def fibonacci(n):
    # 如果 n 小于等于 1,直接返回 n
    if n <= 1:
        return n
    # 否则递归计算前两个斐波那契数的和
    return fibonacci(n-1) + fibonacci(n-2)

# 打印第一次调用 fibonacci(10) 的结果
print(f"fibonacci(10) = {fibonacci(10)}")
# 打印第二次调用 fibonacci(10) 的结果,会使用缓存
print(f"fibonacci(10) = {fibonacci(10)}")

6.3 日志记录装饰器 #

# 导入日志模块
import logging
# 从functools模块导入wraps装饰器
from functools import wraps
# 从datetime模块导入datetime类
from datetime import datetime

# 配置日志基本设置
logging.basicConfig(
    # 设置日志记录级别为INFO
    level=logging.INFO,
    # 设置日志输出格式
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    # 配置日志处理器,输出到文件和终端
    handlers=[
        logging.FileHandler('app.log'),
        logging.StreamHandler()
    ]
)

# 定义用于函数调用日志记录的装饰器
def log_function_call(func):
    # 使用wraps保留原始函数的元数据
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 获取被装饰函数的名称
        func_name = func.__name__
        # 记录函数调用开始的信息,包括参数
        logging.info(f"开始调用函数: {func_name}, 参数: args={args}, kwargs={kwargs}")

        try:
            # 调用实际的被装饰函数
            result = func(*args, **kwargs)
            # 记录函数调用成功和返回值
            logging.info(f"函数 {func_name} 调用成功,返回值: {result}")
            # 返回被装饰函数的结果
            return result
        except Exception as e:
            # 发生异常时,记录异常信息
            logging.error(f"函数 {func_name} 调用失败,异常: {str(e)}")
            # 抛出异常
            raise
    # 返回包装后的函数
    return wrapper

# 使用日志记录装饰器装饰divide_numbers函数
@log_function_call
def divide_numbers(a, b):
    # 判断除数是否为零,如为零则抛出异常
    if b == 0:
        raise ValueError("除数不能为零")
    # 返回除法计算结果
    return a / b

# 测试divide_numbers函数的正常情况
try:
    # 调用divide_numbers进行10除以2
    result = divide_numbers(10, 2)
    # 打印计算结果
    print(f"计算结果: {result}")
except ValueError as e:
    # 捕获并打印错误信息
    print(f"错误: {e}")

# 测试divide_numbers函数的异常情况
try:
    # 调用divide_numbers进行10除以0,会抛出异常
    result = divide_numbers(10, 0)
except ValueError as e:
    # 捕获并打印错误信息
    print(f"错误: {e}")

6.4 权限验证装饰器 #

# 导入wraps用于保持被装饰函数的元数据
from functools import wraps

# 定义角色类
class Role:
    # 初始化方法,name为角色名,permissions为权限列表(可选)
    def __init__(self, name, permissions=None):
        # 保存角色名
        self.name = name
        # 如果没有提供权限列表,则默认为空列表
        self.permissions = permissions or []

    # 为角色添加权限
    def add_permission(self, permission):
        # 如果权限尚未存在于权限列表中,则添加
        if permission not in self.permissions:
            self.permissions.append(permission)

    # 检查角色是否拥有某个权限
    def has_permission(self, permission):
        # 如果权限在权限列表中,则返回True
        return permission in self.permissions

# 定义用户类
class User:
    # 初始化方法,name为用户名,role为用户角色(可选)
    def __init__(self, name, role=None):
        # 保存用户名
        self.name = name
        # 记录用户的角色
        self.role = role  

    # 检查用户是否拥有指定权限,通过其角色判断
    def has_permission(self, permission):
        # 如果用户有关联的角色,则调用角色的has_permission
        if self.role:
            return self.role.has_permission(permission)
        # 如果没有角色,直接返回False
        return False

# 定义权限检查函数
def check_permission(user, required_permission):
    # 检查用户是否有指定权限
    return user.has_permission(required_permission)

# 定义权限验证装饰器
def require_permission(permission):
    # 装饰器工厂,接收需要的权限
    def decorator(func):
        # 使用wraps保持函数元数据
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 判断参数是否符合要求,第二个参数应为用户对象
            if len(args) >= 2 and hasattr(args[1], 'has_permission'):
                user = args[1]
                # 检查用户是否有所需权限
                if not check_permission(user, permission):
                    # 获取角色名,如果没有角色则为“无角色”
                    role_name = user.role.name if user.role else "无角色"
                    # 没有权限时抛出PermissionError异常
                    raise PermissionError(f"用户 {user.name} (角色: {role_name}) 没有权限 {permission}")
            else:
                # 参数错误时抛出ValueError异常
                raise ValueError("函数需要用户对象作为第二个参数")

            # 权限校验通过,调用原始函数
            return func(*args, **kwargs)
        # 返回包装后的函数
        return wrapper
    # 返回装饰器
    return decorator

# 定义管理面板类
class AdminPanel:
    # 删除用户方法,需delete_user权限
    @require_permission('delete_user')
    def delete_user(self, user, target_user):
        # 返回删除结果字符串
        return f"用户 {target_user} 已被 {user.name} 删除"

    # 查看资料方法,需view_profile权限
    @require_permission('view_profile')
    def view_profile(self, user, target_user):
        # 返回查看资料结果字符串
        return f"用户 {user.name} 正在查看 {target_user} 的资料"

# 创建管理员角色,并赋予删除和查看权限
admin_role = Role("管理员", [ 'delete_user', 'view_profile'])
# 创建普通用户角色,仅赋予查看权限
user_role = Role("普通用户", [ 'view_profile'])

# 创建管理员用户,关联管理员角色
admin_user = User("admin", admin_role)
# 创建普通用户,关联普通用户角色
normal_user = User("user1", user_role)

# 打印测试开始信息
print("开始测试权限验证...")
# 创建管理面板实例
panel = AdminPanel()

# 测试管理员删除用户
print("测试管理员删除用户...")
try:
    # 尝试用管理员删除用户
    result = panel.delete_user(admin_user, "test_user")
    # 打印删除结果
    print(result)
except PermissionError as e:
    # 捕获权限异常并输出
    print(f"权限错误: {e}")

# 测试普通用户删除用户(应无权限)
print("测试普通用户删除用户...")
try:
    # 尝试用普通用户删除用户(应该没有权限)
    result = panel.delete_user(normal_user, "test_user")
    # 打印结果
    print(result)
except PermissionError as e:
    # 捕获权限异常并输出
    print(f"权限错误: {e}")

# 测试普通用户查看资料(应有权限)
print("测试普通用户查看资料...")
try:
    # 尝试用普通用户查看资料(应该有权限)
    result = panel.view_profile(normal_user, "test_user")
    # 打印查看结果
    print(result)
except PermissionError as e:
    # 捕获权限异常并输出
    print(f"权限错误: {e}")

# 展示角色权限管理的优势
print("\n=== 角色权限管理演示 ===")
# 打印管理员角色的权限
print(f"管理员角色权限: {admin_role.permissions}")
# 打印普通用户角色的权限
print(f"普通用户角色权限: {user_role.permissions}")

# 动态为普通用户角色添加删除权限
print("\n为普通用户角色添加删除权限...")
# 调用方法为普通用户角色添加delete_user权限
user_role.add_permission('delete_user')
# 打印更新后的权限列表
print(f"更新后普通用户角色权限: {user_role.permissions}")

# 测试普通用户删除用户(更新权限后,应有权限)
print("\n测试普通用户删除用户(更新权限后)...")
try:
    # 普通用户删除用户,此时应有权限
    result = panel.delete_user(normal_user, "test_user")
    # 打印结果
    print(result)
except PermissionError as e:
    # 捕获权限异常并输出
    print(f"权限错误: {e}")

# 创建无角色用户,测试其权限
print("\n测试无角色用户...")
# 新建未关联角色的用户
no_role_user = User("guest", None)
try:
    # 尝试用无角色用户查看资料
    result = panel.view_profile(no_role_user, "test_user")
    # 打印结果
    print(result)
except PermissionError as e:
    # 捕获权限异常并输出
    print(f"权限错误: {e}")

6.5 事务处理装饰器 #

# 导入wraps,用于装饰器保留原函数元信息
from functools import wraps

# 定义一个模拟数据库连接的类
class DatabaseConnection:
    # 定义初始化方法,设置连接和事务的状态
    def __init__(self):
        # 初始化数据库连接对象(模拟,此处为None)
        self.connection = None
        # 初始化事务激活标记为False
        self.transaction_active = False

    # 定义开始事务的方法
    def begin_transaction(self):
        # 设置事务激活标志为True
        self.transaction_active = True
        # 打印事务开始的信息
        print("开始数据库事务")

    # 定义提交事务的方法
    def commit_transaction(self):
        # 如果事务已激活
        if self.transaction_active:
            # 设置事务激活标记为False
            self.transaction_active = False
            # 打印提交事务的信息
            print("提交数据库事务")

    # 定义回滚事务的方法
    def rollback_transaction(self):
        # 如果事务已激活
        if self.transaction_active:
            # 设置事务激活标记为False
            self.transaction_active = False
            # 打印回滚事务的信息
            print("回滚数据库事务")

    # 定义执行SQL语句的方法
    def execute_sql(self, sql, params=None):
        # 如果事务没有激活,抛出异常
        if not self.transaction_active:
            raise Exception("必须在事务中执行SQL")
        # 打印要执行的SQL语句
        print(f"执行SQL: {sql}")
        # 如果有参数,打印参数列表
        if params:
            print(f"参数: {params}")
        # 返回True,表示执行成功(模拟)
        return True

    # 定义更新账户余额的方法
    def update_account_balance(self, account_id, amount_change):
        # 构造SQL语句,用于更新账户余额
        sql = f"UPDATE accounts SET balance = balance + {amount_change} WHERE account_id = ?"
        # 执行SQL语句,并传入参数
        return self.execute_sql(sql, (account_id,))

# 创建数据库连接对象,作为全局对象
db = DatabaseConnection()

# 定义数据库事务处理的装饰器
def database_transaction(func):
    # 保留原函数的元信息
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 开始数据库事务
        db.begin_transaction()
        try:
            # 调用被装饰的函数
            result = func(*args, **kwargs)
            # 调用成功,提交事务
            db.commit_transaction()
            # 返回原函数的结果
            return result
        except Exception as e:
            # 调用过程中出现异常,回滚事务
            db.rollback_transaction()
            # 打印事务回滚的原因
            print(f"事务回滚,原因: {str(e)}")
            # 再次抛出异常
            raise
    # 返回包装后的函数
    return wrapper

# 使用事务处理装饰器修饰转账函数
@database_transaction
def transfer_money(from_account, to_account, amount):
    # 打印转账操作的信息
    print(f"从账户 {from_account} 转账 {amount} 到账户 {to_account}")

    # 检查转账金额是否合法
    if amount <= 0:
        # 金额不合法,抛出异常
        raise ValueError("转账金额必须大于0")

    # 检查源账户是否合法
    if from_account == "invalid_account":
        # 源账户不存在,抛出异常
        raise ValueError("源账户不存在")

    # 从源账户扣除金额
    db.update_account_balance(from_account, -amount)

    # 向目标账户增加金额
    db.update_account_balance(to_account, amount)

    # 打印转账成功的信息
    print("转账成功")
    # 返回True表示转账成功
    return True

# 测试正常转账操作
try:
    # 账户1向账户2转账100元
    result = transfer_money("account1", "account2", 100)
    # 打印转账结果
    print(f"转账结果: {result}")
except Exception as e:
    # 捕获异常并打印失败信息
    print(f"转账失败: {e}")

# 打印分隔信息,准备测试异常情况
print("\n=== 测试异常情况 ===")
try:
    # 调用转账函数,使用无效账户
    result = transfer_money("invalid_account", "account2", 100)
    # 打印转账结果
    print(f"转账结果: {result}")
except Exception as e:
    # 捕获异常并打印失败信息
    print(f"转账失败: {e}")

# 打印分隔信息,准备测试转账负金额
print("\n=== 测试负数金额 ===")
try:
    # 调用转账函数,尝试转账负数金额
    result = transfer_money("account1", "account2", -50)
    # 打印转账结果
    print(f"转账结果: {result}")
except Exception as e:
    # 捕获异常并打印失败信息
    print(f"转账失败: {e}")

6.6 参数验证装饰器 #

# 从functools模块导入wraps,用于保留原函数的元数据
from functools import wraps
# 导入inspect模块,用于获取函数签名
import inspect

# 定义参数验证装饰器,接收多个验证器作为关键字参数
def validate_params(**validators):
    # 定义装饰器函数,接收被装饰的函数func
    def decorator(func):
        # 使用wraps来保留原函数元数据信息
        @wraps(func)
        # 定义包装函数,接收任意位置参数和关键字参数
        def wrapper(*args, **kwargs):
            # 获取被装饰函数的签名对象
            sig = inspect.signature(func)
            # 绑定传入的参数到函数签名
            bound_args = sig.bind(*args, **kwargs)
            # 应用参数的默认值
            bound_args.apply_defaults()
            # 遍历所有需要验证的参数及其验证器
            for param_name, validator in validators.items():
                # 判断参数名是否在绑定参数中
                if param_name in bound_args.arguments:
                    # 获取参数名称对应的值
                    value = bound_args.arguments[param_name]
                    # 执行验证器检查参数值是否合法
                    if not validator(value):
                        # 如果不通过,抛出 ValueError 异常,说明是哪个参数验证失败
                        raise ValueError(f"参数 {param_name} 验证失败: {value}")
            # 所有参数验证通过后,调用被装饰函数
            return func(*args, **kwargs)
        # 返回包装的函数
        return wrapper
    # 返回装饰器函数
    return decorator

# 定义判断值是否为正数的函数
def is_positive(value):
    # 判断值是否为 int 或 float 类型且大于 0
    return isinstance(value, (int, float)) and value > 0

# 定义判断值是否为字符串的函数
def is_string(value):
    # 判断值是否为字符串类型
    return isinstance(value, str)

# 定义判断值是否为合法邮箱的函数
def is_email(value):
    # 判断值是否为字符串且包含 @ 和 .
    return isinstance(value, str) and '@' in value and '.' in value

# 使用参数验证装饰器,对 create_user 函数的参数进行验证
@validate_params(
    # name 参数必须为字符串
    name=is_string,
    # age 参数必须为 0 到 150 之间的整数
    age=lambda x: isinstance(x, int) and 0 < x < 150,
    # email 参数必须为合法邮箱
    email=is_email,
    # salary 参数必须为正数
    salary=is_positive
)
# 定义创建用户的函数
def create_user(name, age, email, salary):
    # 返回包含用户信息的字典
    return {
        'name': name,
        'age': age,
        'email': email,
        'salary': salary
    }

# 测试传入有效参数的情况
try:
    # 创建一个参数均合法的用户
    user = create_user("张三", 25, "zhangsan@example.com", 5000)
    # 打印用户创建成功的信息
    print(f"用户创建成功: {user}")
# 捕获参数验证失败时抛出的异常
except ValueError as e:
    # 打印参数验证失败的原因
    print(f"参数验证失败: {e}")

# 测试传入无效参数的情况
try:
    # 创建用户,参数部分不合法
    user = create_user("", 25, "invalid-email", -1000)
    # 打印用户创建成功的信息
    print(f"用户创建成功: {user}")
# 捕获参数验证失败时抛出的异常
except ValueError as e:
    # 打印参数验证失败的原因
    print(f"参数验证失败: {e}")

6.7 重试机制装饰器 #

# 导入time模块,用于实现延迟
import time
# 从functools模块导入wraps,用于保持装饰器包装后函数的元数据
from functools import wraps
# 导入random模块,用于生成随机数
import random

# 定义一个重试机制装饰器
def retry(max_attempts=3, delay=1, backoff=2, exceptions=(Exception,)):
    # 定义装饰器内部函数,接受被装饰的函数func
    def decorator(func):
        # 用wraps装饰器保持原函数元数据
        @wraps(func)
        # 定义包装函数,接收任意参数
        def wrapper(*args, **kwargs):
            # 初始化重试延迟时间
            current_delay = delay
            # 初始化最后一个异常
            last_exception = None

            # 循环尝试执行函数,最多max_attempts次
            for attempt in range(max_attempts):
                try:
                    # 尝试调用原始函数
                    return func(*args, **kwargs)
                # 捕获指定的异常
                except exceptions as e:
                    # 记录最后一次发生的异常
                    last_exception = e

                    # 如果已经到达最后一次尝试
                    if attempt == max_attempts - 1:
                        # 打印失败信息
                        print(f"函数 {func.__name__} 在 {max_attempts} 次尝试后仍然失败")
                        # 抛出最后一次异常
                        raise last_exception

                    # 打印当前重试失败的信息
                    print(f"函数 {func.__name__} 第 {attempt + 1} 次尝试失败: {str(e)}")
                    # 打印等待时间信息
                    print(f"等待 {current_delay} 秒后重试...")

                    # 等待指定的时间
                    time.sleep(current_delay)
                    # 延迟时间按指数增长(指数退避)
                    current_delay *= backoff

            # 如果所有尝试都失败,抛出最后一次异常
            raise last_exception
        # 返回包装后的函数
        return wrapper
    # 返回装饰器
    return decorator

# 应用重试机制装饰器到unreliable_function函数
@retry(max_attempts=3, delay=1, backoff=2, exceptions=(ValueError, ConnectionError))
# 定义一个不稳定的函数,模拟失败和成功
def unreliable_function():
    # 以30%的概率成功,70%概率失败
    if random.random() < 0.3:
        # 打印成功信息
        print("函数执行成功!")
        # 返回成功结果
        return "成功结果"
    else:
        # 抛出连接异常,模拟网络问题
        raise ConnectionError("网络连接失败")

# 主程序入口:测试重试机制
try:
    # 调用有重试机制的不稳定函数,并获取结果
    result = unreliable_function()
    # 打印最终结果
    print(f"最终结果: {result}")
# 捕获所有异常
except Exception as e:
    # 打印所有尝试都失败的信息
    print(f"所有重试都失败了: {e}")

6.8 监控告警装饰器 #

# 导入time模块,用于计时
import time
# 从functools模块导入wraps函数,用于保持被装饰函数的元信息
from functools import wraps
# 从datetime模块导入datetime类(虽然此处未直接使用)
from datetime import datetime
# 导入logging模块,用于日志记录
import logging

# 创建名为'monitoring'的日志记录器
monitoring_logger = logging.getLogger('monitoring')
# 设置日志级别为INFO
monitoring_logger.setLevel(logging.INFO)
# 创建控制台流式日志处理器
handler = logging.StreamHandler()
# 定义日志输出格式:时间-级别-消息体
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# 设置处理器的日志格式
handler.setFormatter(formatter)
# 将处理器添加到日志记录器
monitoring_logger.addHandler(handler)

# 定义监控函数性能的装饰器
def monitor_performance(threshold_seconds=1.0, alert_on_exception=True):
    # 装饰器实际实现部分,接收被装饰函数
    def decorator(func):
        # 保持函数元信息,定义包装函数
        @wraps(func)
        def wrapper(*args, **kwargs):
            # 记录函数开始执行的时间戳
            start_time = time.time()
            # 获取被装饰函数的名字
            func_name = func.__name__

            try:
                # 调用原始函数并获取返回结果
                result = func(*args, **kwargs)
                # 计算函数实际执行时间
                execution_time = time.time() - start_time
                # 判断执行时间是否超过阈值
                if execution_time > threshold_seconds:
                    # 若超过阈值,记录性能告警日志
                    alert_msg = f"性能告警: 函数 {func_name} 执行时间 {execution_time:.2f}s 超过阈值 {threshold_seconds}s"
                    monitoring_logger.warning(alert_msg)
                else:
                    # 未超过阈值,正常记录执行成功的日志
                    monitoring_logger.info(f"函数 {func_name} 执行成功,耗时 {execution_time:.2f}s")
                # 返回被装饰函数的返回结果
                return result
            except Exception as e:
                # 若出现异常,计算已消耗的时间
                execution_time = time.time() - start_time
                # 如果需要在异常时告警
                if alert_on_exception:
                    # 记录异常告警日志
                    alert_msg = f"异常告警: 函数 {func_name} 执行失败,异常: {str(e)},耗时 {execution_time:.2f}s"
                    monitoring_logger.error(alert_msg)
                else:
                    # 仅记录异常信息的普通日志
                    monitoring_logger.info(f"函数 {func_name} 执行失败,异常: {str(e)},耗时 {execution_time:.2f}s")
                # 重新抛出异常以便外部捕获
                raise
        # 返回包装后的函数
        return wrapper
    # 返回装饰器
    return decorator

# 使用性能监控装饰器,阈值为0.5秒,异常时告警
@monitor_performance(threshold_seconds=0.5, alert_on_exception=True)
def fast_function():
    # 快速函数:休眠0.1秒
    time.sleep(0.1)
    # 返回完成信息
    return "快速执行完成"

# 使用性能监控装饰器,阈值为0.5秒,异常时告警
@monitor_performance(threshold_seconds=0.5, alert_on_exception=True)
def slow_function():
    # 慢速函数:休眠1.0秒
    time.sleep(1.0)
    # 返回完成信息
    return "慢速执行完成"

# 使用性能监控装饰器,阈值为0.5秒,异常时告警
@monitor_performance(threshold_seconds=0.5, alert_on_exception=True)
def error_function():
    # 该函数会主动抛出异常
    raise ValueError("这是一个测试异常")

# 打印分割线,测试快速函数
print("=== 测试快速函数 ===")
# 调用快速函数,获取返回结果
result = fast_function()
# 打印返回的结果
print(f"结果: {result}")

# 打印分割线,测试慢速函数
print("\n=== 测试慢速函数 ===")
# 调用慢速函数,获取返回结果
result = slow_function()
# 打印返回的结果
print(f"结果: {result}")

# 打印分割线,测试异常函数
print("\n=== 测试异常函数 ===")
try:
    # 调用抛出异常的函数,并尝试获取返回结果
    result = error_function()
    # 打印返回的结果(如果调用未抛出异常)
    print(f"结果: {result}")
except Exception as e:
    # 捕获异常,并打印异常信息
    print(f"捕获异常: {e}")

7. 注意事项 #

  1. 使用functools.wraps:保持被装饰函数的元数据
  2. 参数传递:使用*args, **kwargs确保参数正确传递
  3. 返回值处理:确保装饰器正确返回原函数的结果
  4. 装饰器顺序:多个装饰器的应用顺序很重要
  5. 性能考虑:装饰器会增加函数调用开销
  6. 调试困难:过度使用装饰器可能使代码难以调试

8. inspect #

inspect 模块可以帮助我们在装饰器内部获知被装饰函数的参数信息,实现更加灵活和自动化的功能,典型场景是通用参数校验装饰器。 inspect.signature(func) - 获取函数签名,函数签名包含了函数的参数信息:参数名、参数类型、默认值、注解等。

8.1 inspect.signature(func) - 获取函数签名 #

什么是函数签名? 函数签名包含了函数的参数信息:参数名、参数类型、默认值、注解等。

# 导入inspect模块,用于获取函数签名
import inspect

# 定义一个示例函数,包含位置参数、默认参数、可变参数、关键字参数和可变关键字参数
def example_func(name, age=25, *args, email=None, **kwargs):
    pass

# 获取example_func的函数签名对象
sig = inspect.signature(example_func)

print(sig)  # 输出: (name, age=25, *args, email=None, **kwargs)

print(type(sig))  # 输出:<class 'inspect.Signature'>

8.2 签名的结构 #

# 导入inspect模块,用于获取函数签名
import inspect

# 定义一个示例函数,包含位置参数、默认参数、可变参数、关键字参数和可变关键字参数
def example_func(name, age=25, *args, email=None, **kwargs):
    pass

# 获取example_func的函数签名对象
sig = inspect.signature(example_func)

print(sig)  # 输出: (name, age=25, *args, email=None, **kwargs)

print(type(sig))  # 输出:<class 'inspect.Signature'>

# 参数类型
#     POSITIONAL_ONLY 位置参数
#     POSITIONAL_OR_KEYWORD 位置或关键字参数
#     VAR_POSITIONAL 可变位置参数
#     KEYWORD_ONLY 关键字参数
#     VAR_KEYWORD 可变关键字参数
def analyze_signature(func):
    sig = inspect.signature(func)
    print(f"函数 {func.__name__} 的签名: {sig}")

    # 查看参数详情
    for param_name, param in sig.parameters.items():
        print(f"  参数: {param_name}") # 参数名
        print(f"    类型: {param.kind}")   # 参数类型
        print(f"    默认值: {param.default}") # 默认值
        print(f"    注解: {param.annotation}")  # 注解

analyze_signature(example_func)

输出:

(name, age=25, *args, email=None, **kwargs)
<class 'inspect.Signature'>
函数 example_func 的签名: (name, age=25, *args, email=None, **kwargs)
  参数: name
    类型: POSITIONAL_OR_KEYWORD
    默认值: <class 'inspect._empty'>
    注解: <class 'inspect._empty'>
  参数: age
    类型: POSITIONAL_OR_KEYWORD
    默认值: 25
    注解: <class 'inspect._empty'>
  参数: args
    类型: VAR_POSITIONAL
    默认值: <class 'inspect._empty'>
    注解: <class 'inspect._empty'>
  参数: email
    类型: KEYWORD_ONLY
    默认值: None
    注解: <class 'inspect._empty'>
  参数: kwargs
    类型: VAR_KEYWORD
    默认值: <class 'inspect._empty'>
    注解: <class 'inspect._empty'>

8.3 sig.bind(*args, **kwargs) - 参数绑定 #

作用:将实际传入的参数按照函数签名进行绑定,验证参数是否合法。

# 导入inspect模块,用于获取函数签名
import inspect

# 定义一个带有默认参数的简单函数
def func(a, b, c=10):
    return a + b + c

# 获取func函数的签名对象
sig = inspect.signature(func)

# 正确绑定参数的示例
try:
    # 绑定位置参数a=1, b=2,c使用默认值
    bound_args = sig.bind(1, 2)
    # 打印绑定成功后的参数字典
    print("绑定成功:", bound_args.arguments)
except TypeError as e:
    # 如果绑定失败,打印错误信息
    print("绑定失败:", e)

# 错误绑定示例 - 缺少必需参数b
try:
    # 只传递一个参数a=1,缺少b
    bound_args = sig.bind(1)
    # 如果绑定成功,打印参数
    print("绑定成功:", bound_args.arguments)
except TypeError as e:
    # 绑定失败时打印错误信息
    print("绑定失败:", e)

# 使用关键字参数进行绑定
try:
    # 通过关键字参数绑定a=1, b=2,c使用默认值
    bound_args = sig.bind(a=1, b=2)
    # 打印关键字绑定成功后的参数字典
    print("关键字绑定成功:", bound_args.arguments)
except TypeError as e:
    # 关键字绑定失败时打印错误信息
    print("关键字绑定失败:", e)

输出:

绑定成功: {'a': 1, 'b': 2}
绑定失败: missing a required argument: 'b'
关键字绑定成功: {'a': 1, 'b': 2}

8.4 BoundArguments 对象 #

sig.bind() 返回一个 BoundArguments 对象,包含:

# 导入inspect模块,用于获取函数签名相关功能
import inspect

# 定义一个带有默认参数的简单函数
def func(x, y=10, z=20):
    pass

# 获取func函数的签名对象
sig = inspect.signature(func)
# 绑定参数:x=5,z=30,y使用默认值
bound_args = sig.bind(5, z=30)

# 打印bound_args的类型
print("bound_args 类型:", type(bound_args))
# 打印当前绑定的参数(有序字典)
print("bound_args.arguments:", bound_args.arguments)
# 打印位置参数组成的元组
print("bound_args.args:", bound_args.args)
# 打印关键字参数组成的字典
print("bound_args.kwargs:", bound_args.kwargs)
# 打印与BoundArguments对象关联的签名
print("关联的签名:", bound_args.signature)

输出:

bound_args 类型: <class 'inspect.BoundArguments'>
bound_args.arguments: {'x': 5, 'z': 30}
bound_args.args: (5,)
bound_args.kwargs: {'z': 30}
关联的签名: (x, y=10, z=20)

8.5. bound_args.apply_defaults() - 应用默认值 #

作用: 为没有传入值的参数填充默认值。

# 导入inspect模块,用于获取函数签名和参数绑定
import inspect

 # 定义一个带有默认参数的函数
def func(a, b=100, c=200, d=300):
    # 返回格式化的参数字符串
    return f"a={a}, b={b}, c={c}, d={d}"

# 获取func函数的签名对象
sig = inspect.signature(func)

# 只传入部分参数,a=1, c=999,b和d未传递
bound_args = sig.bind(1, c=999)  # a=1, c=999, b和d使用默认值
# 打印应用默认值前的参数绑定情况
print("应用默认值前:", bound_args.arguments)

# 应用参数的默认值,补全未传递的参数
bound_args.apply_defaults()
# 打印应用默认值后的参数绑定情况
print("应用默认值后:", bound_args.arguments)

# 使用绑定参数完整调用原函数
print("完整调用:", func(*bound_args.args, **bound_args.kwargs))

输出:

应用默认值前: {'a': 1, 'c': 999}
应用默认值后: {'a': 1, 'b': 100, 'c': 999, 'd': 300}
完整调用: a=1, b=100, c=999, d=300

8.6 annotations #

__annotations__ 是 Python 函数和方法的一个属性,用于存储参数和返回值的类型提示信息。它的本质是一个字典,键为参数名(或 "return" 表示返回值),值为标注的类型对象。

8.6.1 基本说明 #

  • 如果函数有类型注解,func.__annotations__ 会返回所有参数和返回值的注解;
  • 如果没有注解,则返回的是一个空字典 {};
  • 注解只是一种提示/文档机制,Python 不会自动强制类型校验(除非用第三方工具或自定义装饰器)。

8.6.2 示例 #

def add(x: int, y: int) -> int:
    return x + y

print(add.__annotations__)
# 输出: {'x': <class 'int'>, 'y': <class 'int'>, 'return': <class 'int'>}

你可以通过遍历 __annotations__ 来获取所有注解:

def foo(a: int, b: str = "default", c: float = 3.14) -> bool:
    pass

for name, t in foo.__annotations__.items():
    print(f"{name}: {t}")
# 输出:
# a: <class 'int'>
# b: <class 'str'>
# c: <class 'float'>
# return: <class 'bool'>

8.6.3 在装饰器中的应用 #

装饰器可以通过读取 func.__annotations__,结合 inspect 等模块实现自动的参数类型检查、文档自动生成等高级功能。例如上一节的 type_assert 装饰器,就是使用注解做类型校验的实践案例。

⚠️ 注意:类型注解只是用于类型提示和文档说明,不具备强制执行作用,但能极大提升代码可读性和开发效率。

8.7 自动参数检查 #

下面演示如何结合inspect.signature动态地获取函数参数,实现自动参数检查。 假设我们希望有一个装饰器,可以根据类型注解自动校验传入的实参类型是否正确。如果类型不符,则抛出异常。

# 导入inspect模块用于获取函数签名
import inspect
# 从functools模块导入wraps用于装饰器
from functools import wraps

# 定义类型断言装饰器
def type_assert(func):
    # 获取func的函数签名信息
    sig = inspect.signature(func)

    # 定义包装器函数,保持原函数元信息
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 绑定传入参数到签名上
        bound_args = sig.bind(*args, **kwargs)
        # 应用参数的默认值
        bound_args.apply_defaults()

        # 遍历所有绑定参数进行类型检查
        for name, value in bound_args.arguments.items():
            # 检查该参数名是否有类型注解
            if name in func.__annotations__:
                # 获取该参数的类型注解
                expected_type = func.__annotations__[name]
                # 如果参数实际类型与期望类型不符
                if not isinstance(value, expected_type):
                    # 抛出类型异常并带有详细说明
                    raise TypeError(f"参数 {name} 期望类型 {expected_type.__name__},但收到值 {value!r} (类型: {type(value).__name__})")
        # 参数类型通过检查后,调用原始函数
        return func(*args, **kwargs)

    # 返回包装器函数
    return wrapper

# 使用type_assert装饰器修饰greet函数
@type_assert
def greet(name: str, age: int):
    # 打印问候语,包含名字和年龄
    print(f"大家好,我叫{name},今年{age}岁。")

# 调用greet函数,参数类型正确,正常执行
greet("张三", 25)
# 调用greet函数,age参数类型错误,将抛出TypeError异常
greet("李四", "二十")

输出示例:

大家好,我叫张三,今年25岁。
Traceback (most recent call last):
...
TypeError: 参数 age 期望类型 int,但收到值 '二十' (类型: str)

9.参考回答 #

9.1 什么是装饰器(开门见山) #

装饰器是 Python 中的一种设计模式,用于在不修改原函数代码的前提下增强或修改函数功能。本质上是一个接受函数并返回新函数的高阶函数。

9.2 核心工作原理(展现理解深度) #

  • 装饰器函数接收一个函数作为参数
  • 在内部定义一个包装函数,在调用原函数前后添加逻辑
  • 返回包装后的新函数
  • 使用 @ 语法糖,让代码更简洁

9.3 主要类型(展示知识广度) #

  1. 函数装饰器:装饰普通函数
  2. 带参数的装饰器:需要先调用外层函数返回装饰器
  3. 类装饰器:用类实现,通常实现 __call__ 方法
  4. 装饰类:装饰类本身,常用于单例模式等功能注入

9.4 实际应用场景(体现实战经验) #

  • 性能测试:统计函数执行时间
  • 日志记录:记录函数调用和返回值
  • 缓存机制:缓存函数结果,提升性能
  • 权限验证:在函数执行前进行权限检查
  • 参数验证:校验传入参数是否合法
  • 重试机制:函数失败后自动重试
  • 事务处理:在数据库操作中自动管理事务

9.5 重要注意事项(展现专业素养) #

  1. 使用 functools.wraps:保持被装饰函数的元数据,避免函数名、文档等丢失
  2. 参数传递:使用 *args 和 **kwargs 确保参数正确传递
  3. 返回值处理:确保装饰器正确返回原函数的结果
  4. 装饰器顺序:多个装饰器的应用顺序很重要,从下往上执行
  5. 调试难度:过度使用装饰器可能增加调试复杂度

9.6 加分项(如果时间允许) #

提到 inspect 模块,说明装饰器可以通过函数签名获取参数信息,实现更自动化的功能,比如基于类型注解的自动参数校验。

9.7 回答技巧 #

  • 先说概念,再说原理,最后举例应用
  • 用“比如”“例如”连接,自然引入场景
  • 结束时可以说“这是我理解的核心点,有需要我可以展开”

访问验证

请输入访问令牌

Token不正确,请重新输入