在 Python 中使用装饰器decorator的 7 个层次
在 Python 中使用装饰器的 7 个层次(7 Levels of Using Decorators in Python)
文章目录
- 在 Python 中使用装饰器的 7 个层次(7 Levels of Using Decorators in Python)
- 导言
- Level 0: 了解基本概念Basic Concepts和用法Usages
- 什么是装饰器decorator?
- 我们为什么需要装饰器decorator?
- Level 1: Wrap a Function
- Level 2: 将多个装饰器应用于一个函数
- Level 3: 包装接收参数的函数
- Level 4: 编写可接收参数的装饰器
- Level 5: 保留原始函数Original Functions的元数据Metadata
- Level 6: 保持简单 — 装饰器的设计哲学
掌握 Python 最神奇的功能
导言
在技术面试中,区分初级junior和高级senior Python 程序员的最简单、最快的方法就是让他或她编写装饰器decorator。因为掌握装饰器decorator这个最神奇的 Python 特性是 Python 开发者的一个里程碑milestone。
关于装饰器decorators有很多值得一提的提示tips和技巧tricks,但它们分散在不同的书籍或教程tutorials中,其中一些可能会让初学者更加困惑。这就是我写这篇文章的原因。
本文将分 7 个层次levels深入探讨 Python 装饰器decorators的所有核心概念core concepts、技术和用法。如果您理解了其中的一半,那么阅读包含装饰器的 Python 程序就会变得容易。如果您理解了全部,那么在 Python 中设计和编写装饰器将是小菜一碟piece of cake。
Level 0: 了解基本概念Basic Concepts和用法Usages
什么是装饰器decorator?
装饰器decorator只是 Python 的一个函数式编程特性functional programming feature。装饰器decorator接收一个可调用对象callable(函数function、方法method或类class),并返回一个可调用对象callable。让我们来看一个简单的例子:
def add_author(func):print('Zhang San')return func@add_author
def get_title():return '7 Levels of Using Decorators in Python'print(get_title())
# Zhang San
# 7 Levels of Using Decorators in Python
上述 add_author(func) 函数是一个简单的装饰器decorator。它在接收函数运行前打印作者姓名。
如果函数需要使用该装饰器,我们可以在函数顶部的"@"符号后添加该装饰器。
"@"符号的使用乍一看让人一头雾水,但这只是 Python 的语法糖syntax sugar。我们还可以如下应用装饰器:
get_title = add_author(get_title)
上述方法的结果与"@"方法完全相同:
def add_author(func):print('Zhang San')return funcdef get_title():return '7 Levels of Using Decorators in Python'get_title = add_author(get_title)
print(get_title())
# Zhang San
# 7 Levels of Using Decorators in Python
因此,"@"方法一点也不吓人。它只是为我们提供了一个非常直观、优雅的选择,可以将装饰器应用到函数中。
我们为什么需要装饰器decorator?
装饰器decorator就像一个可重复使用的构件building block,我们可以在需要时将其应用到函数中,而无需编辑函数本身。
如前面的示例所示,只要我们需要在 get_title() 函数前打印作者姓名,就可以直接将该代码块( add_author 装饰器)装配到函数中。无需对 get_title() 函数进行任何修改。如果将来不需要打印作者姓名,只需删除或注释掉 get_title() 顶部的一行即可。
请牢记:“多次编辑一个函数容易出错。而在需要时组装装饰器则既优雅又不会出错。”
简而言之In a nutshell,装饰器decorator为我们提供了很大的灵活性,并将功能方法和主方法分离开来。许多 Python 内置模块和流行的第三方模块都使用了这一强大功能。
Level 1: Wrap a Function
在某些资料中,装饰器decorator也被称为包装器wrapper。因为它可以包装wrap一个函数function并改变其行为。
至于 level 0 中的示例,我们的装饰器decorator只是在 get_title() 执行之前打印了一些内容。我们能做得更多吗?例如更改 get_title() 返回的标题?
当然,如下所示:
def add_things(func):def wrapper():title = func()new_title = title + ' !!!'return new_titlereturn wrapper@add_things
def get_title():return '7 Levels of Using Decorators in Python'print(get_title())
# 7 Levels of Using Decorators in Python !!!
如上所示,我们定义了一个名为 wrapper() 的内部函数inner function,该函数封装了接收到的 func 并在其结果末尾添加了三个感叹号。
基本上,这个示例展示了在 Python 中编写装饰器decorator的通用模板common template。共有 3 个步骤:
- 接收函数function作为参数argument
- 定义一个包装函数wrapper function,对接收到的函数进行处理
- 返回包装函数wrapper function
顺便提一下,在函数式编程functional programming中,我们将包含嵌套函数的装饰器命名为闭包closures。
到目前为止,我们已经了解了装饰器decorators的基本原理。我们还能编写一些简单的装饰器。
遗憾的是,实际需求可能非常复杂,上述基础知识不足以设计出一个强大的装饰器decorator。接下来将介绍更高级的装饰器技术。
Level 2: 将多个装饰器应用于一个函数
由于装饰器decorators被用作功能块functionality block,有时我们希望将许多装饰器decorators集合到一个函数中。如何实现呢?
非常简单,只需将所有需要的装饰器放在函数的顶部,如下所示:
def add_author(func):def wrapper():author = 'Zhang San'return author + '\n' + func()return wrapperdef add_publication(func):def wrapper():pub = 'TechToFreedom'return pub + '\n' + func()return wrapper@add_publication
@add_author
def get_title():return '7 Levels of Using Decorators in Python'print(get_title())
# TechToFreedom
# Zhang San
# 7 Levels of Using Decorators in Python
我们应该注意的一个重要问题是所用装饰器的顺序。如果我们改变上述示例的顺序,结果就会不同:
# Change the order of decorators
@add_author
@add_publication
def get_title():return '7 Levels of Using Decorators in Python'print(get_title())
# Zhang San
# TechToFreedom
# 7 Levels of Using Decorators in Python
事实上,多个装饰器会从下到上逐层包裹函数。上面的代码与下面的代码相同:
def get_title():return '7 Levels of Using Decorators in Python'get_title = add_author(add_publication(get_title))print(get_title())
# Zhang San
# TechToFreedom
# 7 Levels of Using Decorators in Python
Level 3: 包装接收参数的函数
我们之前的示例程序很好,但不够灵活。如果我们在 get_title() 函数中添加一个参数,让它接收一个字符串作为标题,效果会更好。
def get_title(title):return title
但如何修改装饰器以适应这种变化呢?
我们可以让包装函数wrapper function帮助我们接收参数:
def add_author(func):def wrapper(title):author = 'Zhang San'return author + '\n' + func(title)return wrapper@add_author
def get_title(title):return titleprint(get_title('Harry Potter'))
# Zhang San
# Harry Potter
上面的代码已经解决了这个问题,但并不是很强大。
如前所述,装饰器是一个构件building block,可以在需要时添加到其他函数中。但是,我们无法确保所有装配了 add_author 装饰器的函数都只有一个参数。
因此,我们的装饰器decorator是有限的,不能用于包含许多参数的函数。
我们是否需要编写许多类似的装饰器decorators,只是在包装器中使用不同的参数?
幸运的是,我们不必这样做。星号技巧asterisks technique可以让我们的生活更轻松:
def add_author(func):def wrapper(*args, **kwargs):author = 'Zhang San'return author + '\n' + func(*args, **kwargs)return wrapper@add_author
def get_title(title):return titleprint(get_title('Harry Potter'))
# Zhang San
# Harry Potter@add_author
def get_many_title(t1, t2):return t1+'\n'+t2print(get_many_title('Harry Potter 1','Harry Potter 2'))
# Zhang San
# Harry Potter 1
# Harry Potter 2
如上所示,在星号asterisks的帮助下,我们的装饰器decorator可以装配到函数中,而无需考虑函数会收到多少个参数。
这种设计装饰器的方法既受欢迎又值得推荐,因为它能使装饰器更加灵活和强大。
Level 4: 编写可接收参数的装饰器
事实上,上一层的例子还有一个明显的错误:Zhang San is not the author of “Harry Potter”!
我们应该让我们的装饰器更加灵活,这样它就能接收到一个代表 "哈利-波特 "这一真正作者姓名的参数。
现在,事情变得有点复杂:目标函数target functions和装饰器decorator本身都应该接收参数arguments。实现这一任务的想法是在现有装饰器之外添加另一层layer。
def add_author_with_name(author):def add_author(func):def wrapper(*args, **kwargs):return author + '\n' + func(*args, **kwargs)return wrapperreturn add_author@add_author_with_name('J. K. Rowling')
def get_title(title):return titleprint(get_title('Harry Potter'))
# J. K. Rowling
# Harry Potter
如上所述,我们只需在 add_author 装饰器decorator中添加一个外层outer layer 来接收参数argument。
上述程序与以下程序相同:
def add_author_with_name(author):def add_author(func):def wrapper(*args, **kwargs):return author + '\n' + func(*args, **kwargs)return wrapperreturn add_authordef get_title(title):return titleget_title = add_author_with_name('J. K. Rowling')(get_title)print(get_title('Harry Potter'))
# J. K. Rowling
# Harry Potter
Level 5: 保留原始函数Original Functions的元数据Metadata
到目前为止,我们已经设计出了一个非常灵活和强大的装饰器!但真正的高级工程师senior engineer会考虑到所有细节。实际上,装饰器函数decorator function还有一个隐藏的副作用。让我们通过下面的例子来了解一下:
def add_author(func):def wrapper(*args, **kwargs):author = 'Zhang San'return author + '\n' + func(*args, **kwargs)return wrapper@add_author
def get_title(title):"""A func that receives and returns a title."""return titleprint(get_title.__name__)
# wrapper
print(get_title.__doc__)
# None
上述结果与预期不符。 get_title 函数的名称name和 doc 也被包装了!这是装饰器decorators的副作用。
为了避免这种副作用,我们可以手动编写一些类似 wrapper.__name__ = get_title.__name__ 的代码。但还有一个更简单的方法:
from functools import wrapsdef add_author(func):@wraps(func)def wrapper(*args, **kwargs):author = 'Yang Zhou'return author + '\n' + func(*args, **kwargs)return wrapper@add_author
def get_title(title):"""A function that receives and returns a title string."""return titleprint(get_title.__name__)
# get_title
print(get_title.__doc__)
# A function that receives and returns a title string.
如上所示,我们可以在 functools 模块中使用 wraps 装饰器,这将有助于我们保护元数据。在我看来,在每个封装函数中添加 wraps 装饰器是一种很好的做法,可以避免出现意想不到的结果。
Level 6: 保持简单 — 装饰器的设计哲学
如果您达到了这一水平,那么您已经理解了,至少知道了 Python 装饰器decorators的所有核心技术core techniques。
最后但并非最不重要的一点是,在开始为项目设计装饰公司之前,有一个理念值得一提:
Keep it simple and stupid. — A design principle noted by the U.S. Navy
保持简单和愚蠢 — 美国海军指出的设计原则
装饰器decorator是一个优雅的工具elegant tool,可以帮助我们编写干净整洁的 Python 代码。但不要过度使用它或编写过于复杂的装饰器decorator。在我看来,一个具有三层函数three layers of functions的装饰器就足够了,将三个装饰器组装成一个函数也足够了。
俗话说As an old saying goes,过犹不及beyond is as wrong as falling short。我们应始终注意代码的可读性readability,即使使用的是复杂的功能complex feature,也要一切从简。
相关文章:
在 Python 中使用装饰器decorator的 7 个层次
在 Python 中使用装饰器的 7 个层次(7 Levels of Using Decorators in Python) 文章目录 在 Python 中使用装饰器的 7 个层次(7 Levels of Using Decorators in Python)导言Level 0: 了解基本概念Basic Concepts和用法Usages什么是装饰器decorator?我们为什么需要装…...
Vue.js项目部署至Linux服务器的详细步骤
引言 在现代Web开发中,Vue.js作为一款流行的前端框架,为开发者提供了灵活且高效的工具。然而,在将Vue.js项目成功部署到Linux服务器上,可能需要一些额外的步骤和注意事项。本文将深入介绍在Linux服务器上部署Vue.js项目的详细步骤…...
Java三层架构/耦合/IOC/DI
一.三层架构 controller/web 控制层。接收前端发送的请求,对请求进行处理,并响应数据。 service 业务逻辑层,处理具体的业务逻辑。 dao 数据访问层(Data Access Object),也称为持久层。负责数据访问操作,包括数据的增、…...
[调试]stm32使用过程debug记录,持续更新ing
遇到的bug:无法在串口助手接收到stm32向主机输出的数据,串口-USB RX灯不闪烁; 分析:闪烁灯实际上为一个二极管,CH 插入电脑USB接口时,RX处于高电平,当数据传输时,拉低电平导致其闪烁…...
知识付费小程序如何搭建?
随着互联网的发展和人们对知识的渴求,知识付费行业正逐渐崭露头角。而其中,知识付费小程序因其便捷性、个性化等特点,成为了越来越多人的首选。那么,如何搭建一个知识付费小程序呢?本文将为你揭秘从零到一的全过程&…...
springboot整合minio做文件存储
一,minio介绍 MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小&…...
拥抱鸿蒙 - 在展讯T606平台上的探索与实践
前 言 自OpenHarmony 问世后受到了社会各界的广泛关注,OpenHarmony 的生态系统在如火如荼的发展。 酷派作为一家积极拥抱变化的公司,经过一段时间的探索与实践,成功实现将OpenHarmony 系统接入到展讯平台上,我们相信这是一个重要…...
nginx源码分析-1
使用gdb查看函数上下文: gdb attach nginx的work线程 监听端口状态时: 断点打在ngx_http_process_request 并通过浏览器触发请求时:...
超分之SRGAN
Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network使用生成对抗网络的逼真单图像超分辨率一作:Christian Ledig是Twitter2017年的一篇论文。 文章目录 0. 摘要1. 引言1.1 相关工作1.1.1 介绍了SR技术的发展历程1.1.2 介绍了SR…...
Illustrator脚本 #015 自动角线
这是一个在画板上自动生成辅助线和角线的脚本,只要单击最右边按钮运行脚本即可。 绿色的为参考线及出血线。 #target "Illustrator" var settings = {addTrim : true,addBleedGuide : true,addCenterGuide : true,addCover : false,overlapAlert : false,trimma…...
使用Vite创建React + TypeScript(pro和mobile,含完整的空项目结构资源可供下载)
PC端 安装指令: npm create vitelatest react-ts-pro -- --template react-tsVite是一个框架无关的前端工具链,可以快速的生成一个React TS的开发环境,并且可以提供快速的开发体验说明: 1. npm create vitelatest固定写法&#…...
第一次记录QPSK,BSPK,MPSK,QAM—MATLAB实现
最近有偶然的机会学习了一次QPSK防止以后忘记又得找资料,这里就详细的记录一下 基于 QPSK 的通信系统如图 1 所示,QPSK 调制是目前最常用的一种卫星数字和数 字集群信号调制方式,它具有较高的频谱利用率、较强的抗干扰性、在电路上实现也较为…...
每周一算法:区间覆盖
问题描述 给定 N N N个闭区间 [ a i , b i ] [a_i,b_i] [ai,bi],以及一个线段区间 [ s , t ] [s,t] [s,t],请你选择尽量少的区间,将指定线段区间完全覆盖。 输出最少区间数,如果无法完全覆盖则输出 − 1 -1 −1。 输入格式…...
im6ull学习总结(二)Framebuffer 应用编程
1 LCD操作原理 linux中通过framebuffer驱动程序来控制LCD。framebuffer中包含LCD的参数,大小为LCD分辨率xbpp。framebuffer 是一块内存 内存中保存了一帧图像。 关于图像的帧指的是在图像处理中,一帧(Frame)是指图像序列中的单个…...
数据仓库 基本信息
数据仓库基本理论 数据仓库(英语:Data Warehouse,简称数仓、DW),是一个用于存储、分析、报告的数据系统。数据仓库的目的是构建面向分析的集成化数据环境,为企业提供决策支持(Decision Support)…...
仓储革新:AR技术引领物流进入智慧时代
根据《2022年中国物流行业研究:深度探析行业现状(智能设备及智能软件)》,报告中提及:“中国社会物流总额依然保持着较为良好的增长态势,年增速已恢复至常年平均水平。2021年社会物流总额细分中工业物流总额…...
软件仓库部署及应用
随着某公司内部的Linux服务器不断增多,软件更新,系统升级等需求也逐渐凸显。为了提高软 件包管理效率,减少重复下载,公司要求部署一台软件仓库服务器,面向内网提供安装源。 需求描述 > 服务器使用CentOS7操作系统I…...
ASUS华硕ROG幻16笔记本电脑2023款GU604VI VZ VY原装出厂Windows11系统22H2
华硕玩家国度幻16笔记本原厂W11系统,适用型号:GU604VI、GU604VZ、GU604VY 链接:https://pan.baidu.com/s/166x6FNUFEpA3Qbzeory3Hg?pwdlwau 提取码:lwau 系统自带所有驱动、出厂主题壁纸、Office办公软件、MyASUS华硕电脑管…...
可视化云监控/安防监控系统EasyCVR视频管理平台播流失败的原因(端口篇)
安防视频监控EasyCVR平台兼容性强,可支持的接入协议众多,包括国标GB28181、RTSP/Onvif、RTMP,以及厂家的私有协议与SDK,如:海康ehome、海康sdk、大华sdk、宇视sdk、华为sdk、萤石云sdk、乐橙sdk等。平台能将接入的视频…...
边缘检测——PidiNet网络训练自己数据集并优化推理测试(详细图文教程)
PiDiNet 是一种用于边缘检测的算法,它提出了一种简单、轻量级但有效的架构。PiDiNet 采用了新 颖的像素差卷积,将传统的边缘检测算子集成到现代 CNN 中流行的卷积运算中,以增强任务性能。 在 BSDS500、NYUD 和 Multicue 上进行了大量的实验…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
