【python学习笔记】Python装饰器

news/2024/7/7 7:45:56/文章来源:https://www.cnblogs.com/ryukirin/p/18284131

装饰器

参考:

搞懂Python装饰器

Python @wraps 修饰器

装饰器是什么

有兴趣的可以参考PEP 318的原文 Decorators for Functions and Methods 解释了语法用途以及设计出来装饰器的动机

The current method for transforming functions and methods (for instance, declaring them as a class or static method) is awkward and can lead to code that is difficult to understand. Ideally, these transformations should be made at the same point in the code where the declaration itself is made. This PEP introduces new syntax for transformations of a function or method declaration.


Translated by GPT-4o:当前用于转换函数和方法(例如,将它们声明为类方法或静态方法)的方法很笨拙,并且可能导致难以理解的代码。理想情况下,这些转换应该在代码中声明本身的同一位置进行。该PEP引入了一种新的语法,用于函数或方法声明的转换。

原文的例子中表明

@dec2
@dec1
def func(arg1, arg2, ...):pass

等价于

def func(arg1, arg2, ...):pass
func = dec2(dec1(func))

用法

闭包(closure)

只是简单地介绍下,详细的请参考 理解Python闭包概念

闭包的特点:函数内部定义的函数能够记住它们创建时的环境,包括任何在外部函数作用域中可用的变量。

def outer_function(message):msg = [message]def inner_function(person):msg.append(person)print(msg)return inner_functionclosure = outer_function("Hello, World!")
closure("x")
closure("y")
------------------------------------------
['Hello, World!', 'x']
['Hello, World!', 'x', 'y']

闭包和面向对象

闭包比较简洁,也是函数式编程的重要组成部分,但是在可读性方面有所欠缺

面向对象可以通过属性来轻松地在更加复杂的场景中,但是有时候比较重

注意事项

可变类型和不可变类型

参考 可变与不可变类型

内建函数id()返回值的内存地址。

可变对象可以在其id()保持固定不变的情况下改变其取值。
基本数据类型中,列表,集合,字典都是可变数据类型。

如果修改一个对象的值,必须创建新的对象,那么这个对象就是不可变对象。
基本数据类型中,数字,字符串,元组是不可变类型。

变量的引用

inner function中可以访问outer function中的变量,可以修改可变数据类型(如列表、字典),但是不能修改不可变数据类型(如数字、字符串、元组)
我的理解是内部函数并不能改变外部函数变量的地址,但是可以在原址上修改

def outer_function(message):msg = messagedef inner_function(person):msg += personprint(msg)return inner_functionclosure = outer_function("Hello, World!")
closure("x")
closure("y")
------------------------------------------msg += person^^^
UnboundLocalError: cannot access local variable 'msg' where it is not associated with a value

但是可以通过 nonlocal 来告诉Python该变量来自外部函数

def outer_function(message):msg = messagedef inner_function(person):nonlocal msgmsg += personprint(msg)return inner_functionclosure = outer_function("Hello, World!")
closure("x")
closure("y")
------------------------------------------
Hello, World!x
Hello, World!xy
闭包陷阱

还是用一下很经典的例子

# 该例子的本意为记录每次循环中i值的平方
def my_func(*args):fs = []for i in range(3):def func():return i * ifs.append(func)return fs# 故按照该思路预期的结果应该是0 1 4
# 但实际上是4 4 4
fs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())

这里看了一篇博客 循环变量与闭包 其中介绍到

Python中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其他代码块(如ifforwhile)都不会引入新的作用域。同时,Python中的闭包是以引用的方式捕获变量的,所以我们已经可以猜到最终输出是相同值了。

对比该篇博客中的一些其他语言的情况,可以理解为因为Python中的循环变量是重复使用的,且Python中闭包捕获变量是用类似指针的方式指向的该变量的地址。
故没有新的变量,旧变量会随着循环在原地址中一直改变,fs中append进去的也是一直会随着地址中保存的变量改变而改变的变量。
所以最终循环结束,结果都会变为4

根据以上原因,可以使用会新建作用域的lambda

def my_func(*args):fs = []for i in range(3):fs.append((lambda x=i: x * x))return fsfs1, fs2, fs3 = my_func()
print(fs1())
print(fs2())
print(fs3())

返回闭包中不要引用任何循环变量,或者后续会发生变化的变量。

修饰器

装饰器就是一种闭包的应用

我觉得直接看一个例子比较直观,先把整个代码放出来

from functools import wrapsdef the_first(fn):@wraps(fn)def firstly_wrapped():"""wrapped in the first function"""print("in firstly_wrapped")print(fn.__name__)print(fn.__doc__)return 'here is the first function - ' + fn()return firstly_wrappeddef the_second(fn):@wraps(fn)def second_wrapped():"""wrapped in the second function"""print("in second_wrapped")print(fn.__name__)print(fn.__doc__)return 'here is the second function - ' + fn()return second_wrapped@the_first
@the_second
def hello_world():"""hello with two decoration"""return "Hello World"print(hello_world())
-------------------------------------------------------------
in firstly_wrapped # firstly_wrapped中的print文本
hello_world # firstly_wrapped中输出的fn.__name__
hello with two decoration # firstly_wrapped中输出的fn.__doc__(即def下方用""""""围起来的文本)
in second_wrapped # second_wrapped中的print文本
hello_world # second_wrapped中输出的fn.__name__
hello with two decoration # second_wrapped中输出的fn.__doc__
here is the first function - here is the second function - Hello World # hello_world中的return

先看 hello_world 部分

@the_first
@the_second
def hello_world():"""hello with two decoration"""return "Hello World"print(hello_world())

两个装饰器等价于

the_first(the_second(hello_world()))

执行过程就是 the_firstthe_secondhello_world

再看 @wraps(fn) 的部分,这个修饰器的作用是为了 hello_world 不被覆盖,如果两个 @wraps(fn) 都注释掉了的情况会是

in firstly_wrapped # firstly_wrapped中的print文本
second_wrapped # firstly_wrapped中输出的fn.__name__
wrapped in the second function # firstly_wrapped中输出的fn.__doc__
in second_wrapped # second_wrapped中的print文本
hello_world # second_wrapped中输出的fn.__name__
hello with two decoration # second_wrapped中输出的fn.__doc__
here is the first function - here is the second function - Hello World # hello_world中的return

与一开始结果的区别主要在

second_wrapped # firstly_wrapped中输出的fn.__name__
wrapped in the second function # firstly_wrapped中输出的fn.__doc__

是因为此时传进 firstly_wrapped 已经不是 hello_world 了,而是覆盖掉 hello_worldsecond_wrapped

@wraps 装饰器可以把被包装函数的元数据,例如函数名、文档字符串、函数签名等信息保存下来。

可以理解为是将 fn 的信息复制给 firstly_wrapped ,这个函数中出现的 fn 都会是指 fn 本身而不会被覆盖掉

装饰器的应用

可以很轻松地进行一些缓存或者log操作

详情参考:装饰器的应用场景

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/738121.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信云开发数据库连接

//.js文件const db = wx.cloud.database()Page({//页面的初始数据data: {dataObj:"" //定义对象dataObj}, //查询数据getData(){db.collection("pro1").where({ //pro1为数据库名author:"张三" …

Camstar里拿到Grid的行数据

两种方法: 1.GridDataMode属性为Geceric的:拿到的数据直接放在datatable里,并且赋值给grid,这里我把拿来的数据放在了一个集合里,测试用的 随便写写 2.GridDataMode属性为ItemList的:这里是用了一个集合去接收datatable的值,再把集合赋值给grid这里和第一种的区别是我没有…

MQTT专题

什么是Mqtt MQTT协议 全称是(Message Queuing Telemetry Transport),即消息队列遥测传输协议。 是一种基于发布/订阅(Publish/Subscribe)模式的轻量级通讯协议,并且该协议构建于TCP/IP协议之上,我们知道TCP协议本身就具有高可靠性的特点,因此基于其上的MQTT协议同样也…

安装visual studio失败,组策略阻止安装webview2

排查安装和升级问题 - Visual Studio | Microsoft Learn 需要修改注册表 ,将 InstallDefault值改为1注册表路径 HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\EdgeUpdate

appium 使用

refer to: python+appium2~inspector工具使用及定位操作元素~_哔哩哔哩_bilibili 1 APPium Inspector 可以查看Android应用的 包名 和activity 打开APP,且处于启动页面 Q1 noReset=True 设置每次APP启动 不重头 初始化环境 怎么实现呢 Q1 那我们现在用的是appium1 还是 appi…

7月11日云技术研讨会 | 车载信息安全全流程实施方案

7月11日,经纬恒润《车载信息安全全流程实施方案》云技术研讨会,与您相聚云端,不见不散! 伴随着汽车的智能网联化发展,网络攻击也逐渐渗透漫延至汽车领域,汽车行业面临着重大的信息安全挑战。此外,UNECE WP.29 R155和ISO/SAE 21434等标准也对汽车的信息安全提出了规…

基于全数字实时仿真的嵌入式DevOps解决方案

​为丰富浙江省信息技术应用创新(以下简称“信创”)产业生态,在全社会各领域形成示范效应,浙江省经信厅联合省密码管理局开展2023年浙江省深化信创典型案例评选工作。经过征集申报、专家评选、名单公示等程序,确定36个应用示范案例和24个典型解决方案。【典型解决方案】 基…

巴图自动化PN转Modbus RTU协议转换网关模块快速配置

巴图自动化推出Profinet转Modbus网关模块BT-MDPN10,实现Modbus和Profinet设备互联互通。配置简便,提高系统智能化和生产效率。工业领域中常用的通讯协议有:Profinet协议,Modbus协议,ModbusTCP协议,Profibus协议,Profibus DP协议,EtherCAT协议,EtherNET协议,CAN,CanO…

springboot+vue前后端分离项目-项目搭建7-服务器上安装部署

1.下载VMware(个人使用版),官网下载,需要先用邮箱注册,下载地址:https://support.broadcom.com/group/ecx/productdownloads?subfamily=VMware%20Workstation%20Pro 2.下载镜像dvd类型的,华为云官网下载,需要先注册,下载地址:https://mirrors.huaweicloud.com/mirro…

linux进程被杀掉日志,Linux进程突然被杀掉(OOM killer),查看系统日志

Linux进程被杀掉(OOM killer),查看系统日志 基本概念: Linux 内核有个机制叫OOM killer(Out Of Memory killer),该机制会监控那些占用内存过大,尤其是瞬间占用内存很快的进程,然后防止内存耗尽而自动把该进程杀掉。内核检测到系统内存不足、挑选并杀掉某个进程的过程可以参…

Salesforce开发入门指南:零基础学习宝典!

开发人员将Salesforce组织扩展到声明式配置之外,构建应用程序,进而优化业务运营。Salesforce开发人员通常会使用两种编程语言:Apex和JavaScript。 然而,Salesforce开发不仅仅只包括代码。为了在职业道路上脱颖而出,开发人员还需要了解声明性功能,将组织的设计和性能保持最…

Apache SeaTunnel社区首位学生Committer诞生!

采访对象 | 陈炳烨 采访人&编辑 | Debra Chen Apache SeaTunnel社区第一位学生Committer就此诞生!这位来自西安交通大学软件工程专业的同学从较为简单的文档修改工作,逐步深入到代码层面,到最后独立负责开发模块,为Apache SeaTunnel项目的发展添砖加瓦的同时,他本人也…

VMware vSphere Tanzu部署_13_创建TKC集群

1.登录tanzu集群登录语法为:kubectl vsphere login --server= --vsphere-username --insecure-skip-tls-verify$ kubectl vsphere login --server=192.168.203.194 --vsphere-username administrator@vsphere.local --insecure-skip-tls-verify登录示例jianhua@napp:~$ kubec…

“东数西算”长三角(苏州)算力调度中心正式启用!

近日,“东数西算”长三角(苏州)算力调度中心启用暨智算点亮仪式在苏州吴江区举办,在智算产业园举行了算力调度中心启用、智算点亮和智算合作签约。近日,“东数西算”长三角(苏州)算力调度中心启用暨智算点亮仪式在苏州吴江区举办,在智算产业园举行了算力调度中心启用、…

2024 MWC上海,“翼”彩纷呈!

2024年6月26日,2024世界移动通信大会上海(简称“MWC上海”)盛大开幕。围绕“未来先行”主题,来自全球的产业、技术和社区等各界代表齐聚一堂,共话产业高质量发展。 在“智启云领 向新未来”主题馆内,天翼云展出了“息壤”“云骁”“慧聚”三大智算平台和“天翼AI云电脑”…

Ubuntu20.04中 ORBSLAM3的安装和测试

ORBSLAM3 安装以及测试教程(Ubuntu20.04) 1.前期准备工作 1.1安装相关依赖sudo apt install git cmake gcc g++ mlocate1.2下载ORBSLAM3源码 可以直接通过github下载源码: (https://github.com/UZ-SLAMLab/ORB_SLAM3) 2.安装一些所需的库 2.1 安装Eigen3库 直接通过命令安装 su…

vue3 父组件【属性】传值给子组件【props】接收

父组件文件:parentcomponent.vue 子组件文件:childcomponent.vue传普通值 传动态值 传对象 传数组<!-- 父组件 --> <template><h1>I am ParentComponent</h1><ChildComponent msg="nice"/></template> <script setup>im…

K8S学习教程(二):在 PetaExpress KubeSphere容器平台部署高可用 Redis 集群

前言 Redis 是在开发过程中经常用到的缓存中间件,为了考虑在生产环境中稳定性和高可用,Redis通常采用集群模式的部署方式。 在制定Redis集群的部署策略时,常规部署在虚拟机上的方式配置繁琐并且需要手动重启节点,相较之下,使用 PetaExpress 提供的 Kubernetes(k8s) 服务 进…

火山引擎数据飞轮实践:在电商场景中,如何建设全链路数据血缘?

数据作为新型生产要素,正支撑企业的数智化转型。但企业数字化建设也存在管理成本高、数据产品使用门槛高、数据资产价值不够的问题,其原因在于业务和数据之间没有形成双向良性驱动。DataLeap可以帮助企业实现数据驱动,通过具体业务中的数据消费数据,实现决策科学、行动敏捷…