装饰器其实一直是我的一个"老大难"。这个知识点就放在那,但是拖延症。。。

其实在平常写写脚本的过程中,这个知识点你可能用到不多

但在面试的时候,这可是一个高频问题。

一、什么是装饰器

所谓的装饰器,其实就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

这一句话理解起来可能没那么轻松,那先来看一个"傻瓜"函数。

放心,绝对不是"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()函数时,调用过程是这样的:

  1. hello = my_decorator(hello)中,变量hello指向的是my_decorator()
  2. my_decorator(func)中返回的wrapper(),传参是hello,因此又会调用到原函数hello()
  3. 于是乎,先打印出了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次也没印象,读完这篇你就懂了的更多相关文章

  1. 【python】递归听了N次也没印象,读完这篇你就懂了

    听到递归总觉得挺高大上的,为什么呢?因为对其陌生,那么今天就来一文记住递归到底是个啥. 不过先别急,一起来看一个问题:求10的阶乘(10!). 求x的阶乘,其实就是从1开始依次乘到x.那么10的阶乘就 ...

  2. 一篇关于Python装饰器的博文

    这是一篇关于python装饰器的博文 在学习python的过程中处处受阻,之前的学习中Python的装饰器学习了好几遍也没能真正的弄懂.这一次抓住视频猛啃了一波,就连python大佬讲解装饰器起来也需 ...

  3. 万恶之源 - Python装饰器及内置函数

    装饰器 听名字应该知道这是一个装饰的东西,我们今天就来讲解一下装饰器,有的铁子们应该听说,有的没有听说过.没有关系我告诉你们这是一个很神奇的东西 这个有多神奇呢? 我们先来复习一下闭包 def fun ...

  4. Python装饰器总结,带你几步跨越此坑!

    欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...

  5. python装饰器,迭代器,生成器,协程

    python装饰器[1] 首先先明白以下两点 #嵌套函数 def out1(): def inner1(): print(1234) inner1()#当没有加入inner时out()不会打印输出12 ...

  6. Python装饰器及内置函数

    装饰器 听名字应该知道这是一个装饰的东西,我们今天就来讲解一下装饰器,有的铁子们应该听说,有的没有听说过.没有关系我告诉你们这是一个很神奇的东西 这个有多神奇呢? 我们先来复习一下闭包 def fun ...

  7. 关于python装饰器

    关于python装饰器,不是系统的介绍,只是说一下某些问题 1 首先了解变量作用于非常重要 2 其次要了解闭包 def logger(func): def inner(*args, **kwargs) ...

  8. python装饰器通俗易懂的解释!

    1.python装饰器 刚刚接触python的装饰器,简直懵逼了,直接不懂什么意思啊有木有,自己都忘了走了多少遍Debug,查了多少遍资料,猜有点点开始明白了.总结了一下解释得比较好的,通俗易懂的来说 ...

  9. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

随机推荐

  1. ActiveMQ消息队列的使用及应用

    这里就不说怎么安装了,直接解压出来就行了. 谢绝转载,作者保留所有权力 目录:  一:JMQ的两种消息模式 1.1:点对点的消息模式 1.2:订阅模式 二:点对点的实现代码 2.1:点对点的发送端 2 ...

  2. codeforces 459C Pashmak and Buses(模拟,组合数A)

    题目 跑个案例看看结果就知道了:8 2 3 题目给的数据是 n,k,d 相当于高中数学题:k个人中选择d个人排成一列,有多少种不同的方案数,列出其中n中就可以了. #include<iostre ...

  3. dword word byte 相互转换 .xml

    pre{ line-height:1; color:#800080; background-color:#d2c39b; font-size:16px;}.sysFunc{color:#627cf6; ...

  4. 2877: [Noi2012]魔幻棋盘 - BZOJ

    DescriptionInput 第一行为两个正整数N,M,表示棋盘的大小. 第二行为两个正整数X,Y,表示棋盘守护者的位置. 第三行仅有一个正整数T,表示棋盘守护者将进行次操作. 接下来N行,每行有 ...

  5. 数据库设主键以及where的应用

    二.第二课 create table teacher ( tno int primary key identity(1,1),  --将tno设为主键(primary key identity(1,1 ...

  6. 使用ChineseLunisolarCalendar 对象由年份获得生肖名,Datetime.now.tostring获得星期几

    一:使用ChineseLunisolarCalendar 对象由年份获得生肖名,截图 二:代码 using System; using System.Collections.Generic; usin ...

  7. 用jquery向网页添加背景图片 拉伸 模糊 遮罩层 代码

    方法一:手动添加 1.在body内任意位置添加html代码 <div id="web_bg" style=" position:fixed; _position:a ...

  8. 通过yocto给p1010rdb定制linux,并启动linux

    一.通过yocto定制linux 1.安装yocto yocto只能在非root用户下编译,所以先新建一个用户. useradd chen  passwd -d chen 重启电脑进入chen用户. ...

  9. Linux关闭selinux

    最近在折腾Linux服务器,配置环境时,安装php的一个拓展,明明编译成功了,就是加载不进去,phpinfo不显示,查看错误日志是显示加载失败,没权限==,配置ftp程序也会有意想不到的问题,搞了好久 ...

  10. MyBatis之ResultMap简介,关联对象

    MyBatis中在查询进行select映射的时候,返回类型可以用resultType,也可以用resultMap,resultType是直接表示返回类型的,而resultMap则是对外部ResultM ...