【python】装饰器听了N次也没印象,读完这篇你就懂了
装饰器其实一直是我的一个"老大难"。这个知识点就放在那,但是拖延症。。。
其实在平常写写脚本的过程中,这个知识点你可能用到不多
但在面试的时候,这可是一个高频问题。
一、什么是装饰器
所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。
这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。
放心,绝对不是"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)中返回的wrapper(),传参是hello,因此又会调用到原函数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()函数的元信息:
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函数的元信息
输出:
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]
好记性不如烂笔头,写一下理解一下会好很多。
下面还分享类的装饰器,以及装饰器所用场景。
【python】装饰器听了N次也没印象,读完这篇你就懂了的更多相关文章
- 【python】递归听了N次也没印象,读完这篇你就懂了
听到递归总觉得挺高大上的,为什么呢?因为对其陌生,那么今天就来一文记住递归到底是个啥. 不过先别急,一起来看一个问题:求10的阶乘(10!). 求x的阶乘,其实就是从1开始依次乘到x.那么10的阶乘就 ...
- 一篇关于Python装饰器的博文
这是一篇关于python装饰器的博文 在学习python的过程中处处受阻,之前的学习中Python的装饰器学习了好几遍也没能真正的弄懂.这一次抓住视频猛啃了一波,就连python大佬讲解装饰器起来也需 ...
- 万恶之源 - Python装饰器及内置函数
装饰器 听名字应该知道这是一个装饰的东西,我们今天就来讲解一下装饰器,有的铁子们应该听说,有的没有听说过.没有关系我告诉你们这是一个很神奇的东西 这个有多神奇呢? 我们先来复习一下闭包 def fun ...
- Python装饰器总结,带你几步跨越此坑!
欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...
- python装饰器,迭代器,生成器,协程
python装饰器[1] 首先先明白以下两点 #嵌套函数 def out1(): def inner1(): print(1234) inner1()#当没有加入inner时out()不会打印输出12 ...
- Python装饰器及内置函数
装饰器 听名字应该知道这是一个装饰的东西,我们今天就来讲解一下装饰器,有的铁子们应该听说,有的没有听说过.没有关系我告诉你们这是一个很神奇的东西 这个有多神奇呢? 我们先来复习一下闭包 def fun ...
- 关于python装饰器
关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...
- python装饰器通俗易懂的解释!
1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...
- Python 装饰器学习
Python装饰器学习(九步入门) 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...
随机推荐
- 当asp.net core偶遇docker一(模型验证和Rabbitmq 三)
继续上一篇 上一篇,从core方式实现了一个Rabbitmq发送队列消息的接口,我们现在需要在模型验证里面加入验证失败就发送消息的部分 [AttributeUsage(AttributeTargets ...
- Chrome自动格式化Json输出
对JSON格式的内容进行浏览和编辑,以树形图样式展现JSON文档,并可实时编辑 安装 Chrome商店 https://chrome.google.com/webstore/detail/json-h ...
- Android 给服务器发送网络请求
今天听得有点蒙,因为服务器的问题,这边建立服务器的话,学长用的是Idea建立的Spring之类的方法去搞服务器. 然后就是用Android去给这个服务器发送请求,大致效果还是懂的,就是像网站发送请求, ...
- 家庭记账本APP开发准备(二)
今天学习了选项卡,为记账本的分类做了准备.主登录界面进行了优化,但仍未实现各个组件之间的跳转. 选项卡 activity_main.xml <?xml version="1.0&quo ...
- Maven常见异常及解决方法---测试代码编译错误
[ERROR] Please refer to E:\maven\web_nanchang\target\surefire-reports for the individual test result ...
- ACL权限管理 学习
#查看权限 getfacl 文件名 例子:getfacl test 结果:# file: test# owner: root# group: rootuser::rw-group::r-xother: ...
- shazidouhui的使用体验
这个软件有三个界面,一个是我的,一个是已批改作业,一个是未完成作业,但是,我不知道怎么用除了“我的”这个界面以外的其他两个界面,这一点还是需要优化优化,另外就是登陆界面确实还存在一点问题,有待改进.
- Centos搭建go环境以及go入门
引言 本文主要聚焦于 如何在centos上搭建go环境以及go入门, 包括搭建go环境,hello world运行, 创建包等操作,初步入门go语言. 安装环境 在管理员权限下, 也就是root用户 ...
- 解决@ResponseBody不能和 <mvc:annotation-driven>同时使用的问题
我们都知道使用Springmvc的ajax很强大只要三步就可以实现: 1.引入jackson的maven到pom文件: <dependency> <groupId>com.fa ...
- firewall几个常用命令
systemctl status firewalld firewall-cmd --zone=public --add-port=80/tcp --permanent firewall-cmd --r ...