python装饰器理解

news2024/10/6 5:55:35

这篇文章记录了对python装饰器的理解,主要参考了文章【Python】一文弄懂python装饰器(附源码例子),大部分内容是直接转载的,然后根据自己的理解多加了一些解释说明。

python装饰器理解

  • 1 装饰器
  • 2 使用装饰器的动机
  • 3 简单的装饰器
  • 4 装饰器的语法糖@的理解
  • 5 被装饰的函数有参数
  • 6 装饰器有参数
  • 7 类装饰器
  • 8 类装饰器有参数
  • 9 装饰器顺序

1 装饰器

装饰器是给现有的模块增添新的小功能,可以对原函数进行功能扩展,而且还不需要修改原函数的内容,也不需要修改原函数的调用。

装饰器的使用符合了面向对象编程的开放封闭原则。

开放封闭原则主要体现在两个方面:
	1. 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
	2. 对修改封闭,意味着类一旦设计完成,就可以独立其工作,而不要对类尽任何修改。

2 使用装饰器的动机

使用装饰器之前,我们要知道,其实python里是万物皆对象,也就是万物都可传参。
函数也可以作为函数的参数进行传递的。
例子1:

def baiyu():
    print("我是攻城狮白玉")
 
 
def blog(name):
    print('进入blog函数')
    name()
    print('我的博客是 https://blog.csdn.net/zhh763984017')
 
 
if __name__ == '__main__':
    func = baiyu  # 这里是把baiyu这个函数名赋值给变量func
    func()  # 执行func函数
    print('------------')
    blog(baiyu)  # 把baiyu这个函数作为参数传递给blog函数

结果:
在这里插入图片描述
改写代码获取函数的执行时间,代码2:

import time
 
 
def baiyu():
    t1 = time.time()
    print("我是攻城狮白玉")
    time.sleep(2)
    print("执行时间为:", time.time() - t1)
 
 
def blog(name):
    t1 = time.time()
    print('进入blog函数')
    name()
    print('我的博客是 https://blog.csdn.net/zhh763984017')
    print("执行时间为:", time.time() - t1)
 
 
if __name__ == '__main__':
    func = baiyu  # 这里是把baiyu这个函数名赋值给变量func
    func()  # 执行func函数
    print('------------')
    blog(baiyu)  # 把baiyu这个函数作为参数传递给blog函数

结果:
在这里插入图片描述
如果有一个新的函数python_blog_list:

def python_blog_list():
    print('''【Python】爬虫实战,零基础初试爬虫下载图片(附源码和分析过程)
    https://blog.csdn.net/zhh763984017/article/details/119063252 ''')
    print('''【Python】除了多线程和多进程,你还要会协程
    https://blog.csdn.net/zhh763984017/article/details/118958668 ''')
    print('''【Python】爬虫提速小技巧,多线程与多进程(附源码示例)
    https://blog.csdn.net/zhh763984017/article/details/118773313 ''')
    print('''【Python】爬虫解析利器Xpath,由浅入深快速掌握(附源码例子)
    https://blog.csdn.net/zhh763984017/article/details/118634945 ''')

此时,也要测量它的执行时间,则也需要修改为:

def python_blog_list():
    t1 = time.time()
    print('''【Python】爬虫实战,零基础初试爬虫下载图片(附源码和分析过程)
    https://blog.csdn.net/zhh763984017/article/details/119063252 ''')
    print('''【Python】除了多线程和多进程,你还要会协程
    https://blog.csdn.net/zhh763984017/article/details/118958668 ''')
    print('''【Python】爬虫提速小技巧,多线程与多进程(附源码示例)
    https://blog.csdn.net/zhh763984017/article/details/118773313 ''')
    print('''【Python】爬虫解析利器Xpath,由浅入深快速掌握(附源码例子)
    https://blog.csdn.net/zhh763984017/article/details/118634945 ''')
    print("执行时间为:", time.time() - t1)

如果每个新函数都要这么改,显然不太方便,所以可采用装饰器来拓展一些原函数没有的功能。

3 简单的装饰器

定义一个测量执行时间的函数count_time,代码3:

import time
 
 
def baiyu():
    print("我是攻城狮白玉")
    time.sleep(2)
 
 
def count_time(func):
    def wrapper():
        t1 = time.time()
        func()
        print("执行时间为:", time.time() - t1)
 
    return wrapper
 
 
if __name__ == '__main__':
    baiyu = count_time(baiyu)  # 因为装饰器 count_time(baiyu) 返回的是函数对象 wrapper,这条语句相当于  baiyu = wrapper
    baiyu()  # 执行baiyu()就相当于执行wrapper()

结果:
在这里插入图片描述
这里的count_time,返回了一个函数wrapper。把被装饰函数作为参数传递给它,它就返回wrapper函数。不过为了更方便地实现装饰器(即避免baiyu = count_time(baiyu)的使用),可以通过装饰器的语法糖@来实现。

4 装饰器的语法糖@的理解

看了【Python】一文弄懂python装饰器(附源码例子)后,我对@装饰器的理解是:

@A
B
 
#等价于
B
B=A(B)

现在用@实现代码2中的baiyu。代码4:

import time
 
 
def count_time(func):
    def wrapper():
        t1 = time.time()
        func()
        print("执行时间为:", time.time() - t1)
 
    return wrapper
 
 
@count_time
def baiyu():
    print("我是攻城狮白玉")
    time.sleep(2)
'''
等价于
def baiyu():
    print("我是攻城狮白玉")
    time.sleep(2)
baiyu = count_time(baiyu)
'''


 
if __name__ == '__main__':
    baiyu()  # 用语法糖之后,就可以直接调用该函数了

结果:
在这里插入图片描述

5 被装饰的函数有参数

用@实现代码2中的blog。blog带参数,需修改wrapper。代码5:

import time
 
 
def count_time(func):
    def wrapper(*args,**kwargs):
        t1 = time.time()
        func(*args,**kwargs)
        print("执行时间为:", time.time() - t1)
 
    return wrapper

def baiyu():
    print("我是攻城狮白玉")
    time.sleep(2)
 
@count_time
def blog(name):
    print('进入blog函数')
    name()
    print('我的博客是 https://blog.csdn.net/zhh763984017')
'''
等价于
def blog(name):
    print('进入blog函数')
    name()
    print('我的博客是 https://blog.csdn.net/zhh763984017')
blog = count_time(blog) 即 blog = wrapper

'''


 
if __name__ == '__main__':
    blog(baiyu) # wraper(baiyu)

结果:
在这里插入图片描述

6 装饰器有参数

当装饰器也有参数时,比如需要给装饰器函数传入一些备注的信息msg,如何实现呢?
根据前面提到的我对@的理解:

@A
B
 
#等价于
B
B=A(B)

可以得到代码6:

import time
 
 
def count_time_args(msg=None):
    def count_time(func):
        def wrapper(*args, **kwargs):
            t1 = time.time()
            func(*args, **kwargs)
            print(f"[{msg}]执行时间为:", time.time() - t1)
 
        return wrapper
 
    return count_time
 
 
@count_time_args(msg="baiyu")
def fun_one():
    time.sleep(1)
'''
等价于
def fun_one():
    time.sleep(1)
fun_one = count_time_args(msg="baiyu")(fun_one) 
''' 
 
@count_time_args(msg="zhh")
def fun_two():
    time.sleep(1)

'''
等价于
def fun_two():
    time.sleep(1)
fun_two = count_time_args(msg="zhh")(fun_two)
''' 
 
@count_time_args(msg="mylove")
def fun_three():
    time.sleep(1)
'''
等价于
def fun_three():
    time.sleep(1)
fun_three = count_time_args(msg="mylove")(fun_three)
'''  
 
if __name__ == '__main__':
    fun_one() 
    fun_two()
    fun_three()

结果:
在这里插入图片描述

7 类装饰器

前面的装饰器都是函数,装饰器也可以是类,它同样符合:

@A
B
 
#等价于
B
B=A(B)

此时,A是类。当我们初始化类A时,调用的是其__init__(),当我们调用类A时,则是调用其__call__
所以得到代码7:

import time
 
 
class BaiyuDecorator:
    def __init__(self, func):
        self.func = func
        print("执行类的__init__方法")
 
    def __call__(self, *args, **kwargs):
        print('进入__call__函数')
        t1 = time.time()
        self.func(*args, **kwargs)
        print("执行时间为:", time.time() - t1)
 
 
@BaiyuDecorator
def baiyu():
    print("我是攻城狮白玉")
    time.sleep(2)
'''
等价于
def baiyu():
    print("我是攻城狮白玉")
    time.sleep(2)
baiyu=BaiyuDecorator(baiyu) #用baiyu初始化一个BaiyuDecorator
'''
 
def python_blog_list():
    time.sleep(5)
    print('''【Python】爬虫实战,零基础初试爬虫下载图片(附源码和分析过程)
    https://blog.csdn.net/zhh763984017/article/details/119063252 ''')
    print('''【Python】除了多线程和多进程,你还要会协程
    https://blog.csdn.net/zhh763984017/article/details/118958668 ''')
    print('''【Python】爬虫提速小技巧,多线程与多进程(附源码示例)
    https://blog.csdn.net/zhh763984017/article/details/118773313 ''')
    print('''【Python】爬虫解析利器Xpath,由浅入深快速掌握(附源码例子)
    https://blog.csdn.net/zhh763984017/article/details/118634945 ''')
 
 
@BaiyuDecorator
def blog(name):
    print('进入blog函数')
    name()
    print('我的博客是 https://blog.csdn.net/zhh763984017')
'''
等价于
def blog(name):
    print('进入blog函数')
    name()
    print('我的博客是 https://blog.csdn.net/zhh763984017')
blog = BaiyuDecorator(blog) #用blog初始化一个BaiyuDecorator
'''
 
if __name__ == '__main__':
    baiyu()  #调用类BaiyuDecorator的call函数
    print('--------------')
    blog(python_blog_list) #调用类BaiyuDecorator的call函数,传参数python_blog_list

结果:
在这里插入图片描述

8 类装饰器有参数

仍然根据这个来理解:

@A
B
 
#等价于
B
B=A(B)

看代码8,已经加了注释:

class BaiyuDecorator:
    def __init__(self, arg1, arg2):  # init()方法里面的参数都是装饰器的参数
        print('执行类Decorator的__init__()方法')
        self.arg1 = arg1
        self.arg2 = arg2
 
    def __call__(self, func):  # 因为装饰器带了参数,所以接收传入函数变量的位置是这里
        print('执行类Decorator的__call__()方法')
 
        def baiyu_warp(*args):  # 这里装饰器的函数名字可以随便命名,只要跟return的函数名相同即可
            print('执行wrap()')
            print('装饰器参数:', self.arg1, self.arg2)
            print('执行' + func.__name__ + '()')
            func(*args)
            print(func.__name__ + '()执行完毕')
 
        return baiyu_warp
 
 
@BaiyuDecorator('Hello', 'Baiyu')
def example(a1, a2, a3):
    print('传入example()的参数:', a1, a2, a3)
'''
等价于
def example(a1, a2, a3):
    print('传入example()的参数:', a1, a2, a3)
example = BaiyuDecorator('Hello', 'Baiyu')(example) #装饰器通过BaiyuDecorator('Hello', 'Baiyu')进行初始化,再通过(example)调用call函数,得到返回值baiyu_warp。
'''
 
if __name__ == '__main__':
    print('准备调用example()')
    example('Baiyu', 'Happy', 'Coder') # baiyu_warp('Baiyu', 'Happy', 'Coder') 
    print('测试代码执行完毕')

结果:
在这里插入图片描述

9 装饰器顺序

一个函数可以被多个装饰器进行装饰,此时要理解执行顺序,可以再次根据:

@A
B
 
#等价于
B
B=A(B)

即:

@A
@B
C

# 1把@B C看成一个整体
C=(
	@B
	C
	
	return C 
)#这里不符合python语法,只是为了便于理解
@A
C

# 2再把这个整体进行等价处理
C=(
	C
	C = B(C)
	
	return C
)#这里不符合python语法,只是为了便于理解
@A
C

# 3即
C
C=B(C)
@A
C

# 4得
C
C=A(B(C))

所以得到代码9:

def BaiyuDecorator_1(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器1')
 
    return wrapper
 
def BaiyuDecorator_2(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器2')
 
    return wrapper
 
def BaiyuDecorator_3(func):
    def wrapper(*args, **kwargs):
        func(*args, **kwargs)
        print('我是装饰器3')
 
    return wrapper
 
@BaiyuDecorator_1
@BaiyuDecorator_2
@BaiyuDecorator_3
def baiyu():
    print("我是攻城狮白玉")
 
'''
等价于
def baiyu():
    print("我是攻城狮白玉")
baiyu = BaiyuDecorator_1(BaiyuDecorator_2(BaiyuDecorator_3(baiyu))) 
'''
if __name__ == '__main__':
    baiyu()

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决报错:ModuleNotFoundError: No module named ‘timm.optim.novograd‘ 的办法,亲测有效

问题 在尝试运行文件的时候,有这样的引用 from timm.optim.novograd import NovoGrad 总是报错!!! 解决办法 试过 更新timm : pip install --upgrade timm 试过换一种引用方式 from timm.optim import NovoGra…

英伟达 Jetson Xavier/Xavier NX/Orin系统移植编译

英伟达 Jetson Xavier/Xavier NX/Orin系统移植编译 1、下载Jetson BSP包和交叉编译环境 地址:https://developer.nvidia.com/embedded/jetson-linux-archive下载需要版本即可,此次编译采用32.4.2版本 需要下载的文件如下: 2、新建一个文件…

简历提示:如何撰写出色的简历

您的简历可能是您一生中写的最重要的一页。遵循我们的 20 条简历写作技巧,让您的简历取得成功。 您知道一份出色的简历的重要性。这是您获得一份好工作所需的文件,而一份好工作可以带来美好的生活。因此,我们整理了 20 个简历技巧来帮助您撰…

JWT知识

JWT概念 JWT组成 Java实现JWT Header String getHeader() {String header "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";String encodeBase64URLSafeString Base64.encodeBase64URLSafeString(header.getBytes(StandardCharset…

Python数据科学视频讲解:Python字典

2.13 Python字典 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解2.13节内容。本书已正式出版上市,当当、京东、淘宝等平台热销中,搜索书名即可。内容涵盖数据科学应用的全流程,包括数据科学应用…

时序分解 | Matlab实现NGO-ICEEMDAN基于北方苍鹰算法优化ICEEMDAN时间序列信号分解

时序分解 | Matlab实现NGO-ICEEMDAN基于北方苍鹰算法优化ICEEMDAN时间序列信号分解 目录 时序分解 | Matlab实现NGO-ICEEMDAN基于北方苍鹰算法优化ICEEMDAN时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现NGO-ICEEMDAN基于北方苍鹰算法优化ICE…

关联规则分析和相关系数

在第三讲 我们说过了一个皮尔森系数的计算公式 然后在第八讲 我们又看到了一个类似的式子。 这个是属于相关分析的范畴

GZ015 机器人系统集成应用技术样题5-学生赛

2023年全国职业院校技能大赛 高职组“机器人系统集成应用技术”赛项 竞赛任务书(学生赛) 样题5 选手须知: 本任务书共 24页,如出现任务书缺页、字迹不清等问题,请及时向裁判示意,并进行任务书的更换。参赛队…

玩转Docker(五):网络

文章目录 〇、关于linux系统网络一、none网络二、host网络三、bridge网络四、user-defined网络 Docker安装时会自动在host上创建三个网络,我们可用docker network ls命令查看: docker network ls那么这几种网络分别有什么含义呢?在回答这个问…

OpenTiny Vue 组件库3.12.0 发布:文档大优化!增加水印和二维码两个新组件

非常高兴跟大家宣布,2023年11月30日,OpenTiny Vue 发布了 v3.12.0 🎉。 OpenTiny 每次大版本发布,都会给大家带来一些实用的新特性,10.24 我们发布了 v3.11.0 版本,增加了富文本、ColorPicker 等4个新组件…

Python数据科学视频讲解:Python数据清洗基础

3.1 Python数据清洗基础 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解3.1节内容。本书已正式出版上市,当当、京东、淘宝等平台热销中,搜索书名即可。内容涵盖数据科学应用的全流程,包括数据科…

Android : 序列化 Parcelable 简单应用

1.Parcelable 介绍 Parcelable 是 Android 提供的一个序列化接口,用于将数据写入 Parcel,以及从 Parcel 中读取数据。一个类只要实现了这个接口,该类的对象就可以被序列化,主要用于 IPC(进程间通信)、Bind…

在Windows上通过VS2019自带的Cmake来编译OpenCV-4.5.3源码

文章目录 用VS打开OpenCV源码cmake的配置及生成操作生成及安装 用VS打开OpenCV源码 方式一:文件–》打开–》Cmake 找到源码根目录下CMakeLists.txt文件 导入即可。 方式二:在开始使用这里 选择 打开本地文件夹 找到源码的根目录,导入即可…

黑马点评06分布式锁 2Redisson

实战篇-17.分布式锁-Redisson功能介绍_哔哩哔哩_bilibili 1.还存在的问题 直接实现很麻烦,借鉴已有的框架。 2.Redisson用法 3.Redisson可重入原理 在获取锁的时候,看看申请的线程和拿锁的线程是否一致,然后计算该线程获取锁的次数。一个方法…

单链表详解(附图解,结尾附全部源码)

下面开始带大家对单链表的增删查改进行图解 首先给大家介绍一下链表 链表就是每一个结构体中包含一个数据和一个结构体指针,这个指针就相当于锁链的作用将下一个结构体给锁住,但是每个结构体的空间是相对独立的。 图解: 1 首先实现尾插 如果…

XSS漏洞 深度解析 XSS_labs靶场

XSS漏洞 深度解析 XSS_labs靶场 0x01 简介 XSS原名为Cross-site Sciprting(跨站脚本攻击),因简写与层叠样式表(Cascading style sheets)重名,为了区分所以取名为XSS。 这个漏洞主要存在于HTML页面中进行动态渲染输出的参数中,利用了脚本语…

基于java 的经济开发区管理系统设计与实现(源码+调试)

项目描述 临近学期结束,还是毕业设计,你还在做java程序网络编程,期末作业,老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。今天给大家介绍一篇基于java 的经济开发区管…

云演 Can you getshell?

1、扫目录&#xff0c;看看到upload.php,找到上传点 2、只让上传jpg gif png&#xff0c;上传图片写码 <?php eval($_POST[c]);?>这个码不行 换马 <script language"php">eval($_REQUEST[c])</script>3、蚁剑连接、得到flag

解决ZooKeeper中/rmstore无法删除问题

无法删除znode /rmstore的原因在于yarn在注册时候候自己添加上ACL&#xff0c;导致无法直接删除。解决办法&#xff1a;重新设置ACL。 首先&#xff0c;查看ACL&#xff1a;getAcl /rmstore/ZKRMStateRoot 之后&#xff0c;重新设置ACL&#xff1a;setAcl /rmstore/ZKRMState…

【Pytorch】学习记录分享2——Tensor基础,数据类型,及其多种创建方式

pytorch 官方文档 Tensor基础&#xff0c;数据类型&#xff0c;及其多种创建方式 1. 创建 Creating Tensor&#xff1a; 标量、向量、矩阵、tensor2. 三种方法可以创建张量&#xff0c;一是通过列表(list)&#xff0c;二是通过元组(tuple)&#xff0c;三是通过Numpy的数组(arra…