当前位置: 首页 > news >正文

decorator

装饰器这东西,乍一听可能觉得挺玄乎,好像是什么高级魔法。其实说白了,它就是一种包装。咱们平时收快递,包裹外面那层纸箱或者塑料袋,就是个装饰器。里面的东西没变,但多了层保护,或者多了张快递单,功能上多了一点。

在 Python 里,函数就是那个“里面的东西”。有时候你想给这个函数加点料,比如记录一下它什么时候被调用的,或者看看它运行了多久,又或者检查一下参数对不对。最直接的办法,就是打开这个函数,在它的代码里加上几行记录时间的代码。但这么做有点笨,一来破坏了原来的函数,二来如果十个函数都要加,你就得改十次。

这时候装饰器的想法就来了。不如我们做个“盒子”,把函数放进去。这个盒子本身也是个函数,它接收你的原函数,然后在内部定义一个新的函数。在这个新函数里,你可以先做点额外操作(比如记录开始时间),然后调用原来的函数,拿到结果,再做点收尾操作(比如记录结束时间并计算耗时),最后把结果返回出去。从外面看,你调用的还是那个函数名,但它已经具备了新的行为。

举个例子,假设你有个函数,专门用来打招呼。

defsay_hello(name):returnf"Hello,{name}!"

现在想看看执行这个函数要花多久。你可以写一个计时的装饰器。

importtimedeftimer(func):defwrapper(*args,**kwargs):start=time.time()result=func(*args,**kwargs)end=time.time()print(f"{func.__name__}took{end-start:.2f}seconds to run.")returnresultreturnwrapper

怎么用呢?最直观的用法是这样:

say_hello=timer(say_hello)print(say_hello("World"))

运行后,除了打印问候语,还会看到一行时间记录。这里timer就是那个装饰器函数,它吃进去一个say_hello,吐出来一个新的函数wrapper。我们把这个新函数重新赋值给say_hello这个名字。以后调用say_hello,实际上调用的是被包装过的wrapper

Python 觉得这种写法还不够方便,于是提供了@这个语法糖。你可以在函数定义前直接加上@timer

@timerdefsay_hello(name):returnf"Hello,{name}!"

效果和上面那种手动赋值一模一样,但看起来清爽多了。这个@符号,就像是在给函数贴上一个标签,告诉 Python:“喂,在用它之前,先给它套上这个盒子。”

装饰器真正厉害的地方在于它的可组合性。你可以给一个函数套上多个盒子。比如,你既想计时,又想记录日志。那就写两个装饰器,然后叠着用。

@log@timerdefsome_function():...

这时候,some_function会先被timer包装,然后再被log包装。顺序就像穿衣服,是从下往上的,离函数定义最近的先执行。

说到这里,你可能觉得装饰器就是用来增强函数的。这没错,但它的本质其实是“函数变换”。它输入一个函数对象,输出一个新的函数对象。这种对函数本身进行操作和变换的能力,是函数作为“一等公民”特性的直接体现。在 Python 里,函数可以像整数、字符串一样被传递、修改和返回,装饰器是这种理念最优雅的应用之一。

除了用在函数上,装饰器也能用在类上,或者类的方法上。类装饰器相对少见一些,但原理相通,只不过它操作的是类这个对象。

刚开始接触时,可能会被那个嵌套的def wrapper搞糊涂。多写几次,多调试几次,看看执行流程就明白了。关键点在于,装饰器函数(如timer)只在函数定义时执行一次,它返回的wrapper函数才是每次调用时真正执行的东西。

还有一个常遇到的坑是关于函数身份的。被装饰后,函数的__name__等元信息会变成wrapper的,这有时会破坏一些依赖元信息的工具。解决方法是使用functools.wraps这个装饰器,把它加在你写的wrapper函数上,它能帮你把原函数的元信息复制过来。

fromfunctoolsimportwrapsdeftimer(func):@wraps(func)defwrapper(*args,**kwargs):...returnwrapper

这本身也是个很有趣的现象:我们用装饰器来修复另一个装饰器带来的副作用。

在实际项目中,装饰器无处不在。Web框架里用它来定义路由(@app.route('/')),用它来做权限检查;测试框架里用它来标记测试用例;大型应用中用它来实现插件机制或中间件。它把那些横切在各个函数之间的通用逻辑(比如日志、鉴权、事务管理)干净地剥离了出来,让核心业务代码保持简洁。

理解装饰器,算是从“写脚本”到“做工程”的一个小台阶。它背后体现的是一种不直接修改原有对象,而是通过包装和组合来扩展功能的设计思想。这种思想,在很多优秀的软件设计中都能看到影子。下次再看到那个@符号,或许可以想想,它不仅仅是一个语法工具,更是通往更灵活代码组织方式的一扇小门。

http://www.jsqmd.com/news/583716/

相关文章:

  • AVR单片机Vcc电压精确测量库MCUVoltage
  • ESP32轻量级电池电量估算库:基于ADC电压查表的SOC计算方案
  • 在 RAG 驱动的应用中的路由
  • 嵌入式系统中单例模式的应用与实现
  • 2025最权威的十大AI写作网站实测分析
  • context manager
  • Abaqus模拟铝合金搅拌摩擦焊顺序热力耦合过程:残余应力仿真与最优焊接方案对比
  • SerialToProcessing:面向Processing的嵌入式轻量串口协议
  • 2026届学术党必备的AI辅助写作工具推荐榜单
  • 《算法题讲解指南:动态规划算法--子数组系列》--21.乘积最大子数组,22.乘积为正数的最长子数组
  • Go语言的错误处理:从panic到优雅降级
  • 多层PCB内部结构与工艺全解析
  • ARM-Linux与MCU开发的核心差异解析
  • Go语言的并发编程:从Goroutine到Channel
  • C/C++堆栈工作机制与优化实践
  • JTAG接口原理与故障诊断实战指南
  • C语言断言函数:原理、应用与最佳实践
  • NuvIoT_Utils:嵌入式IoT终端轻量级硬件抽象工具库
  • 《掌控习惯》书摘3
  • 嵌入式开发入门:串口通信与Linux操作实战
  • MB85RCxx FRAM嵌入式I²C驱动设计与工程实践
  • Go语言的性能优化:从分析到实践
  • 太卷了,阿里一面试官把多年总结的Java八股文完全开源了.......
  • RP2040 PIO软件串口:高精度轻量级UART实现
  • 危包企业可靠吗?2026年江苏危险品包装安全与创新趋势深度解读 - 2026年企业推荐榜
  • 第16章 案例特训专题【数据库篇】
  • Go语言的HTTP服务器:从基础到高级
  • PING)))超声波测距模块底层驱动开发与高精度TOF测量
  • 【后端】业务逻辑与应用逻辑:构建可维护软件的关键分离
  • MicroPython for mbed经典平台移植深度解析