一、什么是装饰器

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

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

放心,绝对不是"Hello World"!

def hello():print("你好,装饰器")

肿么样,木骗你吧? 哈哈,这个函数不用运行相信大家都知道输出结果:“你好,装饰器”。

那如果我想让hello()函数再实现个其他功能,比如多打印一句话。

那么,可以这样"增强"一下:

def my_decorator(func):def wrapper():print("这是装饰后具有的新输出")func()return wrapperdef hello():print("你好,装饰器")hello = my_decorator(hello)hello()

运行结果:

这是装饰后具有的新输出
你好,装饰器
[Finished in 0.1s]

很显然,这个"增强"没啥作用,但是可以帮助理解装饰器。

当运行最后的hello()函数时,调用过程是这样的:

  1. hello = my_decorator(hello)中,变量hello指向的是my_decorator()
  2. my_decorator(func)中传参是hello,返回的wrapper,因此又会调用到原函数hello()
  3. 于是乎,先打印出了wrapper()函数里的,然后才打印出hello()函数里的

那上述代码里的my_decorator()就是一个装饰器。

它改变了hello()的行为,但是并没有去真正的改变hello()函数的内部实现。

但是,python一直以"优雅"被人追捧,而上述的代码显然不够优雅。

二、优雅的装饰器

所以,想让上述装饰器变得优雅,可以这样写:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def my_decorator(func):def wrapper():print("这是装饰后具有的新输出")func()return wrapper@my_decorator
def hello():print("你好,装饰器")hello()

这里的@my_decorator就相当于旧代码的hello = my_decorator(hello),@符号称为语法糖。

那如果还有其他函数也需要加上类似的装饰,直接在函数的上方加上@my_decorator就可以,大大提高函数的重复利用与可读性。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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]
  1. 多个参数

但是还没完,这样虽然简单,但是随之而来另一个问题:因为并不是所有函数参数都是一样的,
当其他要使用装饰器的函数参数不止这个一个肿么办?比如:

@my_decorator
def hello3(speaker, listener):print("{}对{}说你好!".format(speaker, listener))

没关系,在python里,*args**kwargs表示接受任意数量和类型的参数,所以我们可以这样
写装饰器里的wrapper()函数:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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]
  1. 自定义参数

上面2种,装饰器都是接收外来的参数,其实装饰器还可以接收自己的参数。
比如,我加个参数来控制下装饰器中打印信息的次数:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def count(num):def my_decorator(func):def wrapper(*args, **kwargs):for i in range(num):print("这是装饰后具有的新输出")func(*args, **kwargs)return wrapperreturn my_decorator@count(3)
def hello(people_name):print("你好,{}".format(people_name))hello("老王")

注意,这里count装饰函数中的2个return.
运行下,应该会出现3次:

这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
这是装饰后具有的新输出
你好,老王
[Finished in 0.1s]
  1. 内置装饰器@functools.wrap

现在多做一步探索,我们来打印下下面例子中的hello()函数的元信息:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
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 functoolsdef 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__()
会被执行。

来改造下之前的例子,把函数装饰器改成类装饰器:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class MyDecorator():def __init__(self, func):self.func = funcdef __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():...

但是要注意这里的执行顺序,会从上到下去执行,可以来看下:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:778463939
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def my_decorator(func):def wrapper():print("这是装饰后具有的新输出")func()return wrapperdef my_decorator2(func):def wrapper():print("这是装饰后具有的新输出2")func()return wrapperdef 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]

好记性不如烂笔头,写一下理解一下会好很多。

一篇文章带你了解python装饰器相关推荐

  1. python人脸识别防小偷_一篇文章带你了解Python 人脸识别有多简单

    今天的Python学习教程给大家介绍一个世界上最简练的人脸辨认库 face_recognition,你可以应用 Python 和命令行工具进行提取.辨认.操作人脸. 基于业内领先的 C++ 开源库 d ...

  2. python人脸识别实验报告总结_一篇文章带你了解Python 人脸识别有多简单

    原标题:一篇文章带你了解Python 人脸识别有多简单 今天的Python学习教程给大家介绍一个世界上最简洁的人脸识别库 face_recognition,你可以使用 Python 和命令行工具进行提 ...

  3. 万字长文,一篇文章带你入门Python

    注释 Python中用#表示单行注释,#之后的同行的内容都会被注释掉. 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例 ...

  4. 有没有python的班_【万字长文】别再报班了,一篇文章带你入门Python

    最近有许多小伙伴后台联系我,说目前想要学习Python,但是没有一份很好的资料入门.一方面的确现在市面上Python的资料过多,导致新手会不知如何选择,另一个问题很多资料内容也很杂,从1+1到深度学习 ...

  5. 一篇文章带你了解Python中的游戏开发模块pyglet

    前言 为什么我不选择pygame,原因很简单,因为pyglet 更简单,比较轻量级,就好比django和flask的区别. 相信你在读了这篇文章之后也会毅然决然地选择pyglet. 这篇文章主要围绕p ...

  6. python中self_一个例子带你入门Python装饰器

    ============ 欢迎关注我的公众号:早起python ============ 前言 在还未正式发布的python3.9中,有一个新功能值得关注,那就是任意表达式可以作为装饰器,如果你还不知 ...

  7. python装饰器带参数函数_当我使用带参数的python装饰器时,如何将参数传递给最内部的函数?...

    当Func返回不是真时,我的装饰器用于召回Func.def deco_retry(retry_times): def _deco_retry(func): def wrapper(*args, **k ...

  8. python list find_一篇文章带你了解Python爬虫常用选择器

    原创 麦自香 Python爬虫案例 当我们初学爬虫的时候,我们都会选择一些最基本的网站,往往不带任何反爬措施.比如某个博客站点,我们要爬全站的话,就顺着列表页爬到文章页,再把文章的时间.作者.正文等信 ...

  9. 一篇文章带你了解python数据分析岗位怎么样

    前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 分析目标 各城市对数据分析岗位的需求情况 不同细分领域对数据分析岗的需求情况 数据分析岗位的薪资状况 工作经验与薪水的关系 公 ...

最新文章

  1. Python3中None用法
  2. arcgis栅格计算器python教程_ArcGIS栅格计算器
  3. 欧锦赛球星谱:帕克领豪阵 诺天王对决加索尔
  4. JS中for循环的两种写法
  5. Python的GUI的最终选择Tkinter的初体验
  6. 练习_用if语句实现考试成绩划分
  7. 社交网络初探——链路预测
  8. leetcode570. 至少有5名直接下属的经理(SQL)
  9. word交叉引用插入文献后更新域之后编号未更新
  10. 如何让HTML在手机上实现直接拨打电话以及发送短信?
  11. c语言中的下标,c语言中数组的下标从什么入手下手?_后端开发
  12. Codeforces 700 C. Break Up(Tarjan求桥)
  13. 内部类异常-NoClassDefFoundError
  14. 欧姆龙cp1h指令讲解_欧姆龙cp1h常用指令学习(十五)网络通讯指令SEND,RECV,CMND...
  15. 用Python中的VTK库导入并显示Assembly的STL文件
  16. 团队项目事后诸葛亮会议
  17. 零基础学习Hadoop
  18. 奶茶店的线上线下营销策略
  19. 读《深度思考 让所有事情都能正确入手》
  20. vue数字递增的动画效果

热门文章

  1. 服务器-番外篇-搭建samba共享
  2. Java中浮点数的基础知识
  3. 示例演示数据压缩的效果
  4. links下c语言中for的作用是,C语言开发注意事项
  5. 【MM模块】MRP Running — Lot Size 批量大小
  6. ERP兵法——从案例透视方法(实施篇下)
  7. HANA全面上市,成为SAP史上用户数量增长最快的产品之一
  8. 2020年全国压岁钱榜单出炉,今年小朋友压岁钱归谁?
  9. arm linux内核调试,kgdb在ARM开发板上调试kernel成功
  10. 一个报文的路由器之旅_报文的交换和寻址转发