python中的装饰器是如何工作的_python装饰器工作原理与实例详解

装饰器通过函数作为第一类对象实现,定义一个接收函数的装饰器,在其内部定义wrapper函数并添加额外逻辑,最后返回wrapper;使用@语法糖将原函数替换为包装后的函数,从而在不修改原函数代码的情况下增强功能。

python中的装饰器是如何工作的_python装饰器工作原理与实例详解

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外的功能,装饰器的返回值也是一个函数对象。 它们经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理等等。

装饰器就像给函数穿上了一件“外衣”,这件“外衣”可以给函数增加额外的能力,但不会改变函数本身。

装饰器是如何实现的?

Python的装饰器是基于函数是“第一类对象”这一概念实现的。这意味着函数可以像任何其他对象一样被传递、赋值和作为返回值。

简单来说,装饰器通过以下步骤工作:

立即学习“Python免费学习笔记(深入)”;

定义一个装饰器函数,该函数接收一个函数作为参数。在装饰器函数内部,定义一个新的函数(通常称为wrapper函数)。wrapper函数中,调用原始函数,并在调用前后添加额外的功能。装饰器函数返回wrapper函数。

当使用

@decorator

语法糖来装饰一个函数时,实际上是将原始函数作为参数传递给装饰器函数,并将装饰器返回的wrapper函数赋值给原始函数的名字。

def my_decorator(func):    def wrapper():        print("Something is happening before the function is called.")        func()        print("Something is happening after the function is called.")    return wrapper@my_decoratordef say_hello():    print("Hello!")say_hello()

这段代码的执行结果是:

Something is happening before the function is called.Hello!Something is happening after the function is called.

如何处理带参数的函数?

如果被装饰的函数带有参数,wrapper函数需要能够接收和传递这些参数。可以使用

*args

**kwargs

来实现:

def my_decorator(func):    def wrapper(*args, **kwargs):        print("Before calling the function")        result = func(*args, **kwargs)        print("After calling the function")        return result    return wrapper@my_decoratordef add(a, b):    return a + bprint(add(2, 3))

输出结果:

Before calling the functionAfter calling the function5

装饰器能做什么?除了日志和性能监控

装饰器可以做的远不止日志和性能监控。 它们可以用于:

权限验证: 检查用户是否有权限访问某个函数。缓存: 缓存函数的返回值,避免重复计算。重试机制: 在函数执行失败时自动重试。类型检查: 验证函数参数的类型。信号处理: 在函数执行前后发送信号。路由控制: 在Web框架中,将URL映射到对应的处理函数。

例如,一个简单的缓存装饰器:

import functoolsdef cache(func):    cached_values = {}    @functools.wraps(func)    def wrapper(*args):        if args in cached_values:            return cached_values[args]        else:            result = func(*args)            cached_values[args] = result            return result    return wrapper@cachedef expensive_operation(n):    print(f"Calculating for {n}...")    return n * nprint(expensive_operation(5))print(expensive_operation(5)) # 从缓存中获取

装饰器与类装饰器有什么区别

除了函数装饰器,Python还支持类装饰器。 类装饰器接收一个函数或类作为参数,并返回一个修改后的函数或类。

类装饰器通常通过实现

__call__

方法来工作。 当使用类装饰器装饰一个函数时,实际上是创建了该类的一个实例,并将被装饰的函数作为参数传递给类的构造函数。 当调用被装饰的函数时,实际上是调用了类实例的

__call__

方法。

class MyDecorator:    def __init__(self, func):        self.func = func    def __call__(self, *args, **kwargs):        print("Before calling the function")        result = self.func(*args, **kwargs)        print("After calling the function")        return result@MyDecoratordef say_hello(name):    print(f"Hello, {name}!")say_hello("Alice")

如何编写一个带参数的装饰器?

有时候,我们需要一个可以接收参数的装饰器。 这可以通过创建一个返回装饰器函数的函数来实现。

def repeat(num_times):    def decorator_repeat(func):        def wrapper(*args, **kwargs):            for _ in range(num_times):                result = func(*args, **kwargs)            return result        return wrapper    return decorator_repeat@repeat(num_times=3)def greet(name):    print(f"Hello, {name}!")greet("Bob")

这个例子中,

repeat

函数接收一个

num_times

参数,并返回一个装饰器函数

decorator_repeat

decorator_repeat

函数再接收被装饰的函数

func

,并返回wrapper函数。

装饰器会影响函数的元数据吗?

默认情况下,装饰器会改变被装饰函数的元数据,例如

__name__

__doc__

。 这可能会导致一些问题,例如在使用

help()

函数时显示不正确的信息。

为了解决这个问题,可以使用

functools.wraps

装饰器来保留原始函数的元数据。

import functoolsdef my_decorator(func):    @functools.wraps(func)    def wrapper(*args, **kwargs):        """This is the wrapper function."""        print("Before calling the function")        result = func(*args, **kwargs)        print("After calling the function")        return result    return wrapper@my_decoratordef say_hello():    """This is the original function."""    print("Hello!")print(say_hello.__name__)print(say_hello.__doc__)

使用了

functools.wraps

后,

say_hello.__name__

会输出

say_hello

say_hello.__doc__

会输出

This is the original function.

。 如果没有使用

functools.wraps

,则会输出wrapper函数的信息。

装饰器链:多个装饰器叠加使用

可以同时使用多个装饰器来增强一个函数的功能,这就是装饰器链。 装饰器的应用顺序是从下往上,从里到外。

def bold(func):    def wrapper(*args, **kwargs):        return "" + func(*args, **kwargs) + ""    return wrapperdef italic(func):    def wrapper(*args, **kwargs):        return "" + func(*args, **kwargs) + ""    return wrapper@bold@italicdef get_message(message):    return messageprint(get_message("Hello"))

在这个例子中,

get_message

函数首先被

italic

装饰器装饰,然后再被

bold

装饰器装饰。 因此,输出结果是

Hello

以上就是python中的装饰器是如何工作的_python装饰器工作原理与实例详解的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1370796.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 10:51:38
下一篇 2025年12月14日 10:51:53

相关推荐

  • Python装饰器在嵌套函数调用中避免重复计时输出的策略

    本文探讨了在使用Python装饰器对嵌套函数进行计时时,如何避免因内部函数调用而产生的重复计时输出问题。通过在装饰器内部引入一个调用深度计数器,可以智能地控制计时信息的打印,确保只有指定深度的函数调用才输出计时结果,从而实现更精确和简洁的性能监控。 问题背景:装饰器与嵌套函数调用的冗余输出 在pyt…

    好文分享 2025年12月14日
    000
  • python字典如何遍历数据

    遍历字典可选择不同方法:1. 用.keys()遍历键,2. 用.values()遍历值,3. 用.items()同时获取键值对,4. 直接遍历默认访问键,推荐根据需求选用,其中.items()最常用。 在Python中,字典(dict)是一种非常常用的数据结构,用于存储键值对。遍历字典有多种方式,具…

    2025年12月14日
    000
  • Python函数中列表原地修改的深度解析:理解变量赋值与对象操作

    在Python函数中对列表进行原地修改时,直接对函数形参进行重新赋值(如nums1 = new_list)并不会影响函数外部传入的原始列表对象。这是因为重新赋值使局部变量指向了一个新对象。要实现真正的原地修改,必须操作原始列表对象的内容,例如使用切片赋值nums1[:] = …或列表方法…

    2025年12月14日
    000
  • Python 装饰器:优化嵌套函数计时输出的策略

    本文探讨了在Python中使用装饰器对嵌套函数进行计时时,如何避免因内部函数调用导致的重复输出问题。通过引入一个基于计数器的机制,本教程展示了如何精确控制计时信息的打印深度,确保只在指定调用层级进行输出,从而实现更清晰、更符合预期的日志行为。 装饰器在嵌套函数中的重复输出问题 在python开发中,…

    2025年12月14日
    000
  • PySide6 中连接 DBus 信号的正确实践

    本教程旨在详细阐述如何在 PySide6 应用程序中正确连接到 DBus 信号。文章将深入探讨连接 DBus 信号时常见的两个关键点:确保本地对象在 DBus 上注册,以及 PySide6 中槽函数签名(QtCore.SLOT)的精确使用。通过具体的代码示例,我们将展示如何监听 DBus 系统总线上…

    2025年12月14日
    000
  • Python OpenCV 视频录制:解决0KB文件或损坏问题的教程

    本教程旨在解决使用Python OpenCV进行视频录制时,生成0KB或损坏MP4文件的问题。核心原因在于cv2.VideoWriter的写入分辨率与摄像头实际输出分辨率不匹配。文章将详细指导如何正确获取摄像头实际工作分辨率,并将其应用于视频写入器,确保录制过程顺畅,生成可播放的视频文件。 1. O…

    2025年12月14日
    000
  • Stripe PaymentLink分账机制详解与应用限制

    本文深入探讨了Stripe PaymentLink在实现支付分账时的核心机制,特别是transfer_data参数的使用方法。我们将详细解析如何通过transfer_data将部分支付金额转移至关联账户,并着重强调了对于一次性支付链接,只能指定固定金额进行转移或收取平台费用,而百分比分账功能仅限于订…

    2025年12月14日
    000
  • Python列表原地修改与变量重赋值:函数作用域深度解析

    Python函数中列表修改的常见陷阱 在python编程中,尤其是在处理列表这类可变对象时,开发者常常会遇到一个问题:在函数内部对列表进行操作后,函数外部的原始列表似乎没有发生预期的改变。这通常源于对python变量赋值、对象引用以及原地修改(in-place modification)机制的理解不…

    2025年12月14日
    000
  • PyTorch中矩阵求和操作的高效向量化实现

    本教程深入探讨了如何在PyTorch中高效地向量化处理涉及矩阵求和的复杂操作,以避免低效的Python循环。通过利用PyTorch的广播机制和张量维度操作,我们将展示如何将逐元素计算转化为并行处理,显著提升计算性能和代码简洁性,并讨论数值精度问题。 1. 低效的循环式矩阵操作及其问题 在pytorc…

    2025年12月14日
    000
  • python如何获取用户的输入_python input()函数获取控制台用户输入

    答案:Python中获取用户输入最常用的方法是input()函数,它会暂停程序并等待用户在控制台输入内容后按回车,返回值始终为字符串类型。若需进行数值运算,必须手动将字符串转换为int或float,否则会导致错误;使用时应添加提示信息以提升用户体验,并通过try-except处理类型转换可能引发的V…

    2025年12月14日
    000
  • Python装饰器在嵌套函数中避免重复打印的技巧

    本文探讨了Python中对嵌套函数应用装饰器时,如何避免因内部函数调用而产生的冗余输出。通过在装饰器内部引入一个基于深度计数的机制,可以精确控制何时打印装饰器生成的输出,从而实现只在最外层或指定深度调用时才显示信息,同时保留内部函数独立调用的功能,有效解决了装饰器重复打印的问题。 问题描述 在pyt…

    2025年12月14日
    000
  • Tkinter 控件动态尺寸调整与比例布局:实现自适应界面的最佳实践

    本文探讨了在 Tkinter 应用中实现控件(如 Treeview 列和文本)按比例自适应窗口大小的策略。核心方法是在应用启动时和窗口每次调整大小时,通过绑定主窗口的 事件,主动调用尺寸调整函数,确保界面元素在任何状态下都能保持预设的比例和布局,解决 winfo_width() 初始值不准确的问题。…

    2025年12月14日
    000
  • 使用Pillow库精确裁剪Matplotlib生成图像的白边

    本教程旨在解决使用Matplotlib显示图像后,在保存或下载时出现意外白边的问题。通过介绍Matplotlib尝试方案的局限性,文章核心内容聚焦于利用Pillow(PIL)库进行图像后处理,提供详细的Python代码示例,演示如何加载带有白边的图像,智能检测并裁剪掉多余的白色区域,最终生成无边框的…

    2025年12月14日
    000
  • 使用 GIF 图像作为 Turtle 对象时无法响应点击事件的解决方案

    本文旨在解决在使用 Python Turtle 模块时,将 Turtle 对象设置为 GIF 图像后,无法响应点击事件的问题。通过修改点击事件的处理方式,将 onclick 函数置于点击事件处理函数内部,可以有效地解决该问题,实现 GIF 图像 Turtle 对象的点击交互功能。 在使用 Pytho…

    2025年12月14日
    000
  • 使用 Numba 加速数组统计:guvectorize 的正确使用姿势

    第一段引用上面的摘要: 本文旨在阐述如何使用 Numba 的 guvectorize 装饰器来加速数组统计计算,特别是当输出数组的形状与输入数组不同时。我们将通过示例代码详细解释 guvectorize 的正确用法,并讨论其与 njit 的区别与适用场景,帮助读者理解并掌握 Numba 优化数组操作…

    2025年12月14日
    000
  • BottlePy:根目录静态文件服务与路由优先级管理

    本教程将指导您如何在BottlePy应用中,从服务器的子目录(如public/)提供静态文件,使其在URL路径上表现为根目录文件,同时确保不覆盖其他应用程序路由。核心解决方案在于正确设置路由的定义顺序,确保特定路由优先于通用静态文件路由被匹配。 理解BottlePy静态文件服务 在web开发中,提供…

    2025年12月14日
    000
  • Python字典迭代与列表转换:创建字典列表的正确姿势

    本文旨在解决Python中将字典内容转换为字典列表时的常见误区。我们将探讨直接迭代字典为何只获取键,以及如何利用dict.items()方法正确地获取键值对,并通过列表推导式高效地构建出包含单个键值对的字典列表。同时,文章还将对比分析csv.DictReader等特殊场景下,其默认输出已是字典列表的…

    2025年12月14日
    000
  • 解决ObsPy读取SAC文件时的TypeError:版本兼容性指南

    本教程旨在解决使用ObsPy库读取SAC文件时遇到的TypeError: Unknown format for file错误。该问题通常源于ObsPy库的特定版本兼容性问题,尤其是在版本更新后。文章将提供具体的解决方案,即回退到已知稳定的ObsPy版本,并指导如何进行版本管理,确保SAC数据能够被正…

    2025年12月14日
    000
  • 使用广度优先搜索(BFS)按层级提取Python字典数据

    本文详细介绍了如何利用广度优先搜索(BFS)算法,从一个表示图结构的Python字典中,按层级(迭代次数)提取数据。通过指定起始节点(source_list)和目标节点(target_list),我们将逐步遍历字典,收集每个层级的节点及其邻居,并以结构化的字典形式输出,同时避免重复访问和循环,直至达…

    2025年12月14日
    000
  • 解决 dput 上传 Debian 包时遇到的 SSL 证书验证失败问题

    本文旨在解决使用 dput 工具上传 Debian 包到 GitLab 仓库时遇到的 SSL 证书验证失败问题,特别是当使用自签名证书时。文章将介绍一个有效的临时解决方案,通过修改 dput 的 Python 脚本来绕过 SSL 证书验证,确保包上传过程顺利进行。 问题描述 当开发者尝试使用 dpu…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信