通俗讲解python 装饰器
作者:把苹果v咬哭 发布时间:2022-04-30 08:20:47
装饰器其实一直是我的一个"老大难"。这个知识点就放在那,但是拖延症。。。
其实在平常写写脚本的过程中,这个知识点你可能用到不多
但在面试的时候,这可是一个高频问题。
一、什么是装饰器
所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。
这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。
放心,绝对不是"Hello World"!
def hello():
print("你好,装饰器")
肿么样,木骗你吧? 哈哈,这个函数不用运行相信大家都知道输出结果:"你好,装饰器"。
那如果我想让hello()
函数再实现个其他功能,比如多打印一句话。
那么,可以这样"增强"一下:
def my_decorator(func):
def wrapper():
print("这是装饰后具有的新输出")
func()
return wrapper
def hello():
print("你好,装饰器")
hello = my_decorator(hello)
hello()
运行结果:
这是装饰后具有的新输出
你好,装饰器
[Finished in 0.1s]
很显然,这个"增强"没啥作用,但是可以帮助理解装饰器。
当运行最后的hello()
函数时,调用过程是这样的:
hello = my_decorator(hello)
中,变量hello指向的是my_decorator()
my_decorator(func)
中传参是hello
,返回的wrapper
,因此又会调用到原函数hello()
于是乎,先打印出了
wrapper()
函数里的,然后才打印出hello()
函数里的
那上述代码里的my_decorator()
就是一个装饰器。
它改变了hello()
的行为,但是并没有去真正的改变hello()
函数的内部实现。
但是,python一直以"优雅"被人追捧,而上述的代码显然不够优雅。
二、优雅的装饰器
所以,想让上述装饰器变得优雅,可以这样写:
def my_decorator(func):
def wrapper():
print("这是装饰后具有的新输出")
func()
return wrapper
@my_decorator
def hello():
print("你好,装饰器")
hello()
这里的@my_decorator
就相当于旧代码的hello = my_decorator(hello)
,@符号称为语法糖。
那如果还有其他函数也需要加上类似的装饰,直接在函数的上方加上@my_decorator
就可以,大大提高函数
的重复利用与可读性。
def my_decorator(func):
def wrapper():
print("这是装饰后具有的新输出")
func()
return wrapper
@my_decorator
def hello():
print("你好,装饰器")
@my_decorator
def hello2():
print("你好,装饰器2")
hello2()
输出:
这是装饰后具有的新输出
你好,装饰器2
[Finished in 0.1s]
三、带参数的装饰器
1. 单个参数
上面的只是一个非常简单的装饰器,但是实际场景中,很多函数都是要带有参数的,比如hello(people_name)。
其实也很简单,要什么我们就给什么呗,直接在对应装饰器的wrapper()
上,加上对应的参数:
def my_decorator(func):
def wrapper(people_name):
print("这是装饰后具有的新输出")
func(people_name)
return wrapper
@my_decorator
def hello(people_name):
print("你好,{}".format(people_name))
hello("张三")
输出:
这是装饰后具有的新输出
你好,张三
[Finished in 0.1s]
2. 多个参数
但是还没完,这样虽然简单,但是随之而来另一个问题:因为并不是所有函数参数都是一样的,
当其他要使用装饰器的函数参数不止这个一个肿么办?比如:
@my_decorator
def hello3(speaker, listener):
print("{}对{}说你好!".format(speaker, listener))
没关系,在python里,*args
和**kwargs
表示接受任意数量和类型的参数,所以我们可以这样
写装饰器里的wrapper()
函数:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("这是装饰后具有的新输出")
func(*args, **kwargs)
return wrapper
@my_decorator
def hello(people_name):
print("你好,{}".format(people_name))
@my_decorator
def hello3(speaker, listener):
print("{}对{}说你好!".format(speaker, listener))
hello("老王")
print("------------------------")
hello3("张三", "李四")
同时运行下hello("老王")
,和hello3("张三", "李四")
,看结果:
这是装饰后具有的新输出
你好,老王
------------------------
这是装饰后具有的新输出
张三对李四说你好!
[Finished in 0.1s]
3. 自定义参数
上面2种,装饰器都是接收外来的参数,其实装饰器还可以接收自己的参数。
比如,我加个参数来控制下装饰器中打印信息的次数:
def count(num):
def my_decorator(func):
def wrapper(*args, **kwargs):
for i in range(num):
print("这是装饰后具有的新输出")
func(*args, **kwargs)
return wrapper
return my_decorator
@count(3)
def hello(people_name):
print("你好,{}".format(people_name))
hello("老王")
注意,这里count
装饰函数中的2个return
.
运行下,应该会出现3次:
这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
[Finished in 0.1s]
4. 内置装饰器@functools.wrap
现在多做一步探索,我们来打印下下面例子中的hello()函数的元信息:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("这是装饰后具有的新输出")
func(*args, **kwargs)
return wrapper
@my_decorator
def hello(people_name):
print("你好,{}".format(people_name))
print(hello.__name__) #看下hello函数的元信息
输出:
wrapper
这说明了,它不再是以前的那个 hello()
函数,而是被 wrapper()
函数取代了。
如果我们需要用到元函数信息,那怎么保留它呢?这时候可以用内置装饰器@functools.wrap
。
import functools
def my_decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print("这是装饰后具有的新输出")
func(*args, **kwargs)
return wrapper
@my_decorator
def hello(people_name):
print("你好,{}".format(people_name))
print(hello.__name__)
运行下:
hello
[Finished in 0.1s]
四、类装饰器
装饰器除了是函数之外,也可以是类。
但是类作为装饰器的话,需要依赖一个函数__call__(),当调用这个类的实例时,函数__call__()就
会被执行。
来改造下之前的例子,把函数装饰器改成类装饰器:
class MyDecorator():
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("这是装饰后具有的新输出")
return self.func(*args, **kwargs)
# def my_decorator(func):
# def wrapper():
# print("这是装饰后具有的新输出")
# func()
# return wrapper
@MyDecorator
def hello():
print("你好,装饰器")
hello()
运行:
这是装饰后具有的新输出
你好,装饰器
[Finished in 0.1s]
跟函数装饰器一样,实现一样的功能。
五、装饰器的嵌套
既然装饰器可以增强函数的功能,那如果有多个装饰器,我都想要怎么办?
其实,只要把需要用的装饰器都加上去就好了:
@decorator1
@decorator2
@decorator3
def hello():
...
但是要注意这里的执行顺序,会从上到下去执行,可以来看下:
def my_decorator(func):
def wrapper():
print("这是装饰后具有的新输出")
func()
return wrapper
def my_decorator2(func):
def wrapper():
print("这是装饰后具有的新输出2")
func()
return wrapper
def my_decorator3(func):
def wrapper():
print("这是装饰后具有的新输出3")
func()
return wrapper
@my_decorator
@my_decorator2
@my_decorator3
def hello():
print("你好,装饰器")
hello()
运行
这是装饰后具有的新输出
这是装饰后具有的新输出2
这是装饰后具有的新输出3
你好,装饰器
[Finished in 0.1s]
好记性不如烂笔头,写一下理解一下会好很多。
来源:https://www.cnblogs.com/pingguo-softwaretesting/p/13522909.html


猜你喜欢
- 在php中获取数组长度方法很简单,php为我们提供了两个函数可以计算一维数组长度,如count,sizeof都可以直接统计数组长度哦,下面我
- 关于窗口函数的基础,请看文章SQL窗口函数许多常见的聚合函数也可以作为窗口函数使用,包括AVG()、SUM()、COUNT()、MAX()以
- 有时候,我们在某一重要的时间段需要监控某张表的变化情况,包含插入、更新、删除。举例来说,当我们把数据导出到外部的系统时,我们希望导出的是全部
- 给明文密码加密的流程:import base64pwd_after_encrypt = base64.b64encode(b'thi
- 本文实例讲述了Python实现简单拆分PDF文件的方法。分享给大家供大家参考。具体如下:依赖pyPdf处理PDF文件切分pdf文件使用方法:
- 本文实例讲述了Python实现基于socket的udp传输与接收功能。分享给大家供大家参考,具体如下:udp的传输与接收windows网络调
- 本节为读者讲解如何利用ADO.NET本身的参数对象和存储过程技术防止注入攻击,以达到用户界面输入与原始SQL的分离,使黑客无法拼接SQL语句
- 起源当同一个远程服务器有多个人使用的时候,想知道服务器是否有人在用,我们不能直接的去登录,因为这样可能会把对方挤下来,这并不友好,所以这里提
- 目录多线程(连接池)操作MySQL插入数据1.主要模块2.创建连接池3.数据预处理4.线程任务5.启动多线程6.完整示例7.思考/总结多线程
- reflect 反射包针对反射,Go 提供了 reflect 包,使用这个包里的函数可以在程序运行时获取和更新未知变量的值,操作未知变量的方
- 说明MySql社区版从5.7.11开始支持基于表的数据加密方案,模块名为keyring_file,支持加密整张表。这种是加密方式其实是基于文
- 目录查找算法 -- 简介顺序查找二分查找插值查找斐波那契查找树表查找1、二叉树查找算法。2、平衡查找树之2-3查找树(2-3 Tree)3、
- 所以对应的asp处理代码如下代码如下:dedearr=split(xiangguanid2,chr(13)) '分割成数组
- 为你的网站,博客等添加rss聚合功能,给出rss.asp和rss.xml两种的聚合代码看过的朋友可帮忙顶哦,这些代码都是第一次发的,外面很多
- 我们学习编程,在学习的时候,会有想把有用的知识点保存下来,我们可以把知识点的内容爬下来转变成pdf格式,方便我们拿手机可以闲时翻看,是很方便
- WordPress 的插件机制实际上只的就是这个 Hook 了,它中文被翻译成钩子,允许你参与 WordPress 核心的运行,是一个非常棒
- 开篇语本文主要是回顾下从项目创建到生成数据到数据库(代码优先)的全部过程。采用EFCore作为ORM框架。本次示例环境:vs2019、net
- 经常需要通过python代码来提取文本的关键词,用于文本分析。而实际应用中文本量又是大量的数据,如果使用单进程的话,效率会比较低,因此可以考
- 前言我们经常需要将大量数据保存起来以备后续使用,数据库是一个很好的解决方案。在众多数据库中,MySQL数据库算是入门比较简单、语法比较简单,
- 查看python内部模块命令,内置函数,查看python已经安装的模块命令可以用dir(modules)或者用 pip list或者用 he