【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 ...
随机推荐
- 【NOIP2015四校联训Day7】 题 题解(Tarjan缩点+DFS)
前言:没错,这题的名字就这么直白.我们考试题. ------------------ 你需要完成$n$道题目.有一些题目是相关的,当你做一道题的时候,如果你做过之前对它有帮助的题目,你会更容易地做出它 ...
- Kaggle-pandas(4)
Grouping-and-sorting 教程 映射使我们可以一次将整个列中的数据转换为DataFrame或Series中的一个值. 但是,通常我们希望对数据进行分组,然后对数据所在的组进行特定的操作 ...
- SpringBoot2.x集成Redis (StringTemplate与redisTemplate的用法)
1. Redis介绍Redis数据库是一个完全开源免费的高性能Key-Value数据库.它支持存储的value类型有五种,包括string(字符串).list(链表).set(集合).zset(sor ...
- 16、Java中级进阶 面向对象 封装
1.封装概述 封装可以被认为是一个保护屏障,将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过类提供的方法来实现对隐藏信息的操作访问,可以有效的防止该类的代码和数据被其他类随意访问. 要访问 ...
- C#LeetCode刷题之#35-搜索插入位置(Search Insert Position)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3979 访问. 给定一个排序数组和一个目标值,在数组中找到目标值, ...
- JavaScript 防抖(debounce)和节流(throttle)
防抖函数 触发高频事件后,n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间 /** * * @param {*} fn :callback function * @param {* ...
- Idea + maven搭建SSM框架
1.打开idea,新建项目,选择maven骨架,然后填写包名和项目名称,一直下一步. 接着下一步,配置项目的maven环境 创建完成之后,如下图:(上边的项目名字只是例子,具体的以自己搭建的项目名为准 ...
- Golang Gtk+3教程:Grid布局
在上个例子中我们使用了box布局,现在让我们来学习另一种布局--grid.其实这几种布局都大同小异,如果你看懂了上一个例子,想必使用grid也不是难事. 程序运行效果: package main im ...
- Mybatis-05-使用注解开发
使用注解开发 1 面向接口编程 原因: 解耦.可扩展性.提高复用性 关于接口的理解 定义与实现的分离 两类接口 一个个体的抽象,abstract class 一个个体某个方面的抽象,interface ...
- centos7 nginx yum 配置
[nginx] name=nginx repo baseurl=http://nginx.org/packages/centos/7/$basearch/ gpgcheck= enabled=