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

Python装饰器的使用详解

目录

1、函数装饰器

1.1、闭包函数

1.2、装饰器语法

1.3、装饰带参数的函数

1.4、被装饰函数的身份问题

1.4.1、解决被装饰函数的身份问题

1.5、装饰器本身携带/传参数

1.6、嵌套多个装饰器

2、类装饰器

装饰器顾名思义作为一个装饰的作用,本身不改变被装饰对象的原本功能,只是对被装饰对象扩展了额外的功能,装饰器分为两类:函数装饰器类装饰器

1、函数装饰器

说明:函数装饰器是一个函数接受另一个函数作为参数的函数,它包装了函数的行为,并返回包装后的函数。所以装饰器的目的是为了扩展函数的功能,而不是修改函数本身,装饰器器的本质就是闭包函数。下面说明一下什么是闭包函数:

1.1、闭包函数

闭包的定义:在函数嵌套的前提下,内部函数使用外部函数的变量,并且外部函数返回内部函数
我们把这个使用外部函数变量的内部函数称为闭包。

闭包函数的作用:变量在函数运行之后销毁,但有的时候需要这个函数的变量,这时候使用闭包函数解决。

闭包函数的三要素:

  • 1.实现函数嵌套
  • 2.内部函数使用外部函数的变量
  • 3.外部函数返回内部函数

我们来看一个示例:

def wrapper_hello(func: callable):def wrapper():print("this is start")func()print("this is end")return wrapperdef hello():print("hello world")# 闭包函数的使用
hello_new = wrapper_hello(hello)
# 函数的调用
hello_new()

运行结果:

解释:
上面的代码中定义了两个函数,其中hello()函数是一个普通函数,它的功能是打印hello world。
我们来看看wrapper_hello()函数具体实现了什么功能:

  • 1.首先它的参数func是一个可调用对象。
  • 2.然后它的内部定义了一个函数wrapper(),并把wrapper对象作为返回值。
  • 3.wrapper()函数内部执行过程:
    • 先打印输出了“this is start”
    • 然后执行func()
    • 最后打印输出"this is end"

所以我们可以说wrapper_hello()函数扩展了hello()函数的功能,hello()原本实现的功能并没有改变。

1.2、装饰器语法

说明:Python提供了一种语法来定义装饰器。称为糖语法(通过@修饰目标函数), 它可以将修饰后函数赋值给修饰函数本身,所以调用函数时,还是直接调用,装饰器只是给函数增加额外的功能,本身并不改变函数功能和调用执行方式。
示例:

def wrapper_hello(func: callable):def wrapper():print("this is start")func()print("this is end")return wrapper# 装饰器糖语法写法
@wrapper_hello
def hello():print("hello world")# 还是正常的调用函数
hello()# 调用等价于
# hello = wrapper_hello(hello)
# hello()

1.3、装饰带参数的函数

说明:因为Python不允许装饰器接受被装饰对象的参数,所以要想实现装饰带参数的函数,在装饰器内部函数中使用 *args 和 **kwargs 来实现。

示例:

def wrapper_hello(func: callable):def wrapper(*args, **kwargs):print("this is start")# 注意如果func函数有返回值,需要使用一个对象来接受返回值,不然func执行完成后就销毁了,也就得不到它原本的返回值。res = func(*args, **kwargs)print("fun执行结果:", res)print("this is end")return resreturn wrapper# 装饰器糖语法写法
@wrapper_hello
def hello(name):return "hello world %s"%name# 还是正常的调用函数
hello("Tom")

注意:要想获取目标函数的返回值结果,必须要在装饰器内部返回执行结果,否则无法获取执行的结果,原因是func函数执行完之后就会被销毁,所以需要在装饰器内部保存目标函数的执行结果。

1.4、被装饰函数的身份问题

说明:如果查看修饰后函数的名字,或者使用内置的help函数查看,发现被修饰函数的名字是wrapper。因为Python认为现在的函数是装饰器函数的内部函数。

示例:

def wrapper_hello(func: callable):def wrapper(*args, **kwargs):print("this is start")# 注意如果func函数有返回值,需要使用一个对象来接受返回值,不然func执行完成后就销毁了,也就得不到它原本的返回值。res = func(*args, **kwargs)print("fun执行结果:", res)print("this is end")return resreturn wrapper# 装饰器糖语法写法
@wrapper_hello
def hello(name):return "hello world %s"%name# 查看hello函数的名字
print(hello.__name__)
help(hello)

运行结果:

1.4.1、解决被装饰函数的身份问题

说明:可以使用functools.wraps装饰器解决这个问题,它的作用是保留原函数的信息。 这可以帮助我们在运行时获取原本对象的信息,比如函数的名字,参数等。

注:也可以显示使用wrapper.__name__ = func.__name__的方法实现

示例:

def wrapper_hello(func: callable):@functools.wraps(func)def wrapper(*args, **kwargs):print("this is start")# 注意如果func函数有返回值,需要使用一个对象来接受返回值,不然func执行完成后就销毁了,也就得不到它原本的返回值。res = func(*args, **kwargs)print("fun执行结果:", res)print("this is end")return res# 等价于@functools.wraps(func)的作用# wrapper.__name__ =  func.__name__return wrapper# 装饰器糖语法写法
@wrapper_hello
def hello(name):return "hello world %s"%name# 查看hello函数的名字
print(hello.__name__)
help(hello)

运行结果:

1.5、装饰器本身携带/传参数

说明:为了更好地理解装饰器参数的必要性, 我们实现一个repeat装饰器,它接受一个数字作为输入。这个装饰器的功能是:重复执行目标函数给定的次数。

示例1:

import functools
def repeat(num_times):def inner_repeat(func):@functools.wraps(func)def wrapper(*args, **kwargs):for i in range(num_times):result = func(*args, **kwargs)return resultreturn wrapperreturn inner_repeat@repeat(num_times=3)
def hello(name):print('hello {}'.format(name))hello('Tom')

示例2:

import functools
def repeat1(num_times):def repeat():def inner_repeat(func):@functools.wraps(func)def wrapper(*args, **kwargs):for i in range(num_times):result = func(*args, **kwargs)return resultreturn wrapperreturn inner_repeat# 注意这里返回的内部repeat函数的调用return repeat()@repeat1(num_times=3)
def hello(name):print('hello {}'.format(name))hello('Tom')

执行结果都为以下结果:

解释:
装饰器执行原理:不管装饰器嵌套了多少层函数,执行顺序是从最外层的函数开始执行也就是repeat1函数,原因是理解为队列,遵循先进先出的原理,所以从最外层的函数先执行。
所以若要装饰器可以传参数最多只需要嵌套3层即可,再嵌套就显得多余和没有必要。
注意:

  • 1、装饰器函数多层嵌套,需要每层都要有返回值,即每层返回对应的函数名。
  • 2、函数括号的参数属于整个函数内部的“全局变量”,也就是不管函数内部嵌套了多少层函数,都可以使用这些变量

1.6、嵌套多个装饰器

说明:通过堆叠的方式将多个装饰器应用到一个函数上。 这些装饰器按照顺序从上到下开始执行

示例:

import functools
# 装饰器嵌套
def start_end(func):@functools.wraps(func)def wrapper(*args, **kwargs):print('this is start')result = func(*args, **kwargs)print('this is end')return resultreturn wrapperdef debug(func):@functools.wraps(func)def wrapper(*args, **kwargs):args_repr = [repr(a) for a in args]kwargs_repr = [f"{k} = {v!r}" for k, v in kwargs.items()]signature = ", ".join(args_repr + kwargs_repr)# print(signature)print(f"calling {func.__name__} ({signature})")result = func(*args, **kwargs)print(f" {func.__name__!r} returned {result!r}")return resultreturn wrapperdef hello(func):@functools.wraps(func)def wrapper(*args, **kwargs):print("这里是hello函数开始")result = func(*args, **kwargs)print("这里是hello函数结束")return wrapper@start_end
@debug
@hello
def say_hello(name):res = f'hello {name}'print(res)return ressay_hello("张三")

运行结果:

解释:

从运行结果可以看出,多重装饰器的嵌套是从上至下开始执行的,但是并不是等一个装饰器执行完了再执行下一个,而是从第一个装饰器开始执行到目标函数停止继续寻找下一个装饰器执行,然后执行第二个装饰器同样执行到目标函数停止继续寻找下一个装饰器执行,按照该方式继续执行,直到执行到最后一个装饰器,才开始执行目标函数,然后层层返回到最外层。

执行原理:就是遵循的栈的先进后出原理。

注意:目标函数不管嵌套了多少层装饰器,目标函数有且仅执行一次。

2、类装饰器

说明:我们也可以使用类作为装饰器。 但是必须实现__call__()方法,目的是使我们的对象可调用。 类装饰器通常用于维护状态。

示例:我们记录函数被调用的次数。 __call__本质上和wrapper()方法是一样的。 它添加了一些功能,执行函数,并返回其结果。

注意:这里我们使用functools.update_wrapper()而不是functools.wraps()来保留我们的函数的信息。

示例:

import functoolsclass CountCallNums:def __init__(self, func):functools.update_wrapper(self,func)self.func = funcself.count = 0def __call__(self, *args, **kwargs):self.count += 1print(f"函数{self.func.__name__} 被执行了 {self.count}次")return self.func(*args, **kwargs)@CountCallNums
def hello():print("hello")hello()
hello()# 上面装饰器等价于
# hello = CountCallNums(hello)
# hello()
# hello()print(hello.__name__)
help(hello)

运行结果:

相关文章:

Python装饰器的使用详解

目录 1、函数装饰器 1.1、闭包函数 1.2、装饰器语法 1.3、装饰带参数的函数 1.4、被装饰函数的身份问题 1.4.1、解决被装饰函数的身份问题 1.5、装饰器本身携带/传参数 1.6、嵌套多个装饰器 2、类装饰器 装饰器顾名思义作为一个装饰的作用,本身不改变被装…...

基于springboot+vue的党员教育和管理系统

博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 ​主要内容:毕业设计(Javaweb项目|小程序|Pyt…...

三个伪类让你的CSS代码更加优雅

公众号:程序员白特,欢迎一起交流学习~ 原文:CSS整洁之道——:is()、:where()和:has()的用法 - 掘金 (juejin.cn) 让我们写出优雅界面的CSS,它也总是把自己进化得更加优雅。 今天我们花5分钟时间学习三个优雅的CSS伪类&#xff1a…...

幻兽帕鲁联机服务器搭建

幻兽帕鲁联机服务器搭建 开通云服务器 云主机购买|香港云服务器|香港云主机|美国云服务器|弹性云主机租用尽在-特网科技 建议选择4核心 16G内存 10M带宽,可满足6-15人游玩 下载安装脚本 windows系统: 下载 http://downinfo.myhostadmin.net/palserver/install…...

京东商品优惠券API获取商品到手价

item_get_app-获得JD商品详情原数据 公共参数 请求地址: jd/item_get_app 名称类型必须描述keyString是调用key(必须以GET方式拼接在URL中)secretString是调用密钥api_nameString是API接口名称(包括在请求地址中)[item_search,i…...

Flutter Version Manager (FVM): Flutter的版本管理终极指南

Flutter笔记 Flutter Version Manager (FVM) - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netEmail: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263/article/details/136300307 my-websit…...

Docker技术概论(3):Docker 中的基本概念

Docker技术概论(3) Docker 中的基本概念 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://…...

死记硬背spring bean 的生命周期

1.bean的生命周期 我们平常经常使用类似于new Object()的方式去创建对象,在这个对象没有任何引用的时候,会被gc给回收掉。而对于spring而言,它本身存在一个Ioc容器,就是用来管理对象的,而对象的生命周期也完全由这个容…...

海外网红营销策略:如何将红人粉丝有效转化为品牌忠实粉丝?

在数字化时代,社交媒体已经成为品牌推广和营销的不可或缺的一部分。随着海外网红营销的崛起,品牌们纷纷将目光投向这个新兴的营销领域。然而,仅仅依赖网红的影响力来达到品牌传播的效果是不够的,更为重要的是如何将网红的粉丝转化…...

java之Bean对象

1. 什么是Bean? Bean被实例化的,是被Spring框架所管理的Java对象。 Spring容器会自动完成Bean的实例化。将所创建的的Bean自动注入到Ioc容器中以供调用。 spring框架中 IOC容器中管理的对象就是Bean对象 2. 第三方bean Bean 因为第三方bean&#xff0…...

Flink——芒果TV的实时数仓建设实践

目录 一、芒果TV实时数仓建设历程 1.1 阶段一:Storm/Flink JavaSpark SQL 1.2 阶段二:Flink SQLSpark SQL 1.3 阶段三:Flink SQLStarRocks 二、自研Flink实时计算调度平台介绍 2.1 现有痛点 2.2 平台架构设计 三、Flink SQL实时数仓分…...

卸载云服务器上的 MySQL 数据库

执行以下命令以停止 MySQL 服务: sudo service mysql stop执行以下命令以彻底卸载 MySQL: sudo apt-get remove --purge mysql-server mysql-client mysql-common sudo apt-get autoremove sudo apt-get autoclean 这将删除 MySQL 数据库服务器、客…...

AUTOSAR SPI详解

1.SPI通信 1)SPI通信脚 SCLO:串行时钟sclk。MTSR:主向从方向数据MTSR(主发送从接收)。MRST:从向主方向数据MRST(主接收从发送)。SLSO:从选择信号SLS,支持16路片选控制。 2) SPI状态机 2.SPI通信波形 在主…...

SpringBoot快速入门(黑马学习笔记)

需求 需求:基于SpringBoot的方式开发一个Web应用,浏览器发起请求/hello后,给浏览器返回字符串"Hello World~"。 开发步骤 第一步:创建SpringBoot工程项目 第二步:定义HelloController类,添加方…...

压力测试工具Jmeter的下载与使用

1、进入官网下载Jmeter https://jmeter.apache.org/ 国内镜像(下载的慢的话可以用国内镜像下载) https://mirrors.cloud.tencent.com/apache/jmeter/binaries/ 2、跳转到下载页面 3、根据不同系统下载相应版本的Jmeter压缩包,Linux系统下载…...

kubectl 陈述式资源管理方法

目录 陈述式资源管理方法 项目的生命周期 1.创建kubectl create命令 2.发布kubectl expose命令 service的4的基本类型 查看pod网络状态详细信息和 Service暴露的端口 查看关联后端的节点 ​编辑 查看 service 的描述信息 ​编辑在 node01 节点上操作,查看…...

从 iOS 设备恢复数据的 20 个iOS 数据恢复工具

作为 iPhone、iPad 或 iPod 用户,您可能普遍担心自己可能会丢失存储在珍贵 iOS 设备中的所有宝贵数据。数据丢失的原因多种多样,这里列出了一些常见原因: 1. iOS 软件更新 2. 恢复出厂设置 3. 越狱 4. 误操作删除数据 5. iOS 设备崩溃 …...

cpp基础学习笔记03:类型转换

static_cast 静态转换 用于类层次结构中基类和派生类之间指针或者引用的转换。up-casting (把派生类的指针或引用转换成基类的指针或者引用表示)是安全的;down-casting(把基类指针或引用转换成子类的指针或者引用)是不安全的。用于基本数据类型之间的转换&#xff…...

H3C OSPF 外部路由引入实验

H3C OSPF 外部路由引入实验 实验拓扑 实验需求 按照图示配置 IP 地址R1,R2,R3 运行 OSPF 使内网互通,所有接口(公网接口除外)全部宣告进 Area 0;要求使用环回口作为 Router-id业务网段不允许出现协议报文…...

ARM简介

ARM:ARM是Advanced RISC Machine的缩写,意为高级精简指令集计算机。 英国ARM公司,2016年被软银创始人孙正义斥资320亿美元收购了。现在是软银旗下的芯片设计公司,总部位于英国剑桥,专注于设计芯片,卖芯片生…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

规则与人性的天平——由高考迟到事件引发的思考

当那位身着校服的考生在考场关闭1分钟后狂奔而至&#xff0c;他涨红的脸上写满绝望。铁门内秒针划过的弧度&#xff0c;成为改变人生的残酷抛物线。家长声嘶力竭的哀求与考务人员机械的"这是规定"&#xff0c;构成当代中国教育最尖锐的隐喻。 一、刚性规则的必要性 …...

OCR MLLM Evaluation

为什么需要评测体系&#xff1f;——背景与矛盾 ​​ 能干的事&#xff1a;​​ 看清楚发票、身份证上的字&#xff08;准确率>90%&#xff09;&#xff0c;速度飞快&#xff08;眨眼间完成&#xff09;。​​干不了的事&#xff1a;​​ 碰到复杂表格&#xff08;合并单元…...