装饰器-初级

  在不改变原有函数逻辑功能的基础上,为函数添加新的逻辑功能。使代码可读性更高、结构更加清晰、冗余度更低

  • 简介
  1. """
  2. 闭包: 函数嵌套的格式就是闭包。写装饰器的闭包是外层函数的返回值就是内层函数
  3. 装饰器:一种特殊的闭包 加上 语法糖[@语法]组成的
  4. 其作用:在不修改原有功能的基础上,为该功能添加其他新的需求。不管在函数中添加多少装饰,函数的名字没有变化
  5.  
  6. 在函数中的使用
  7. 装饰器的特殊闭包:在外层函数中声明一个内层函数,外等函数的返回值是内层函数
  8. 外层函数的参数是:func 接受是给哪一个函数添加装饰
  9. def out(func): 给nomal函数装饰时,此时 func === nomal() | 给diff函数装饰时,此时 func === diff()
  10. def inner():
  11. pass 装饰器的新增功能是 写在内层函数中的,写在外层函数没有效果
  12. return inner
  13.  
  14. 需添加装饰的函数:在普通函数添加装饰器的语法糖,@特殊闭包的外层函数名
  15. @out
  16. def nomal():
  17. pass
  18. @out
  19. def diff()
  20. pass
  21. """
  22. # 特殊的闭包
  23. def outter():
  24. print("这是一个外部函数")
  25. def inner():
  26. # 装饰器的新增功能是 写在内层函数中的
  27. print("这是一个内部函数")
  28. return inner # 外部函数的返回值是内部函数
  29. res = outter()
  30. print('res =',res) # res指向的是内部函数的内存地址。
  31. # 执行结果:这是一个外部函数 res = <function outter.<locals>.inner at 0x1027e88c8>
  32. res()
  33. # 执行结果 :这是一个内部函数。 调用res函数等价于 在outter内部直接调用innner函数
  34.  
  35. """======================================================================"""
  • 实例
  1. # 在不改变原有函数的基础上,为函数增加新的功能。
  2.  
  3. # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
  4. def get_time(func): # func的作用是 明确为哪一个函数添加装饰器;为哪个函数添加就代表哪一个函数
  5. def inner():
  6. import time
  7. start_time = time.time()
  8. # 执行被装饰的功能函数
  9. func()
  10. end_time = time.time()
  11. print("消耗的时间为:",end_time - start_time)
  12. return inner
  13.  
  14. @get_time
  15. def print_table():
  16. for row in range(1,10):
  17. for col in range(1,row + 1):
  18. print(col, "*" ,row, "=", row*col, end="\t")
  19. print()
  20.  
  21. # 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
  22. print_table()
  1. """
  2. 1 * 1 = 1
  3. 1 * 2 = 2 2 * 2 = 4
  4. 1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
  5. 1 * 4 = 4 2 * 4 = 8 3 * 4 = 12 4 * 4 = 16
  6. 1 * 5 = 5 2 * 5 = 10 3 * 5 = 15 4 * 5 = 20 5 * 5 = 25
  7. 1 * 6 = 6 2 * 6 = 12 3 * 6 = 18 4 * 6 = 24 5 * 6 = 30 6 * 6 = 36
  8. 1 * 7 = 7 2 * 7 = 14 3 * 7 = 21 4 * 7 = 28 5 * 7 = 35 6 * 7 = 42 7 * 7 = 49
  9. 1 * 8 = 8 2 * 8 = 16 3 * 8 = 24 4 * 8 = 32 5 * 8 = 40 6 * 8 = 48 7 * 8 = 56 8 * 8 = 64
  10. 1 * 9 = 9 2 * 9 = 18 3 * 9 = 27 4 * 9 = 36 5 * 9 = 45 6 * 9 = 54 7 * 9 = 63 8 * 9 = 72 9 * 9 = 81
  11. 消耗的时间为: 0.0011630058288574219
  12. """
  1. # 在不改变原有函数的基础上,为函数增加新的功能。
  2.  
  3. # 装饰器的闭包,实现功能是:新增计算该功能函数的执行时间
  4. def get_time(func): # func的作用是明确给哪一个函数添加装饰器
  5. def inner():
  6. import time
  7. start_time = time.time()
  8. # 执行被装饰的功能函数
  9. func()
  10. end_time = time.time()
  11. print("消耗的时间为:",end_time - start_time)
  12. return inner
  13.  
  14. """九九乘法表"""
  15. @get_time
  16. def print_table():
  17. for row in range(1,10):
  18. for col in range(1,row + 1):
  19. print(col, "*" ,row, "=", row*col, end="\t")
  20. print()
  21. # 不管给该功能函数添加了多少装饰,执行的时候还是调用该函数的名字
  22. print_table()
  23. """九九加法表"""
  24. @get_time
  25. def print_add_table():
  26. for row in range(1,10):
  27. for col in range(1,row + 1):
  28. print(col, "+" ,row, "=", row+col, end="\t")
  29. print()
  30. print_add_table()

多个函数添加同一个装饰器

装饰器的执行流程

  • 基本流程
  1. def get_time(func): # func的作用是明确给哪一个函数添加装饰器
  2. print("这是装饰器的外层函数。在添加@语法糖时执行,且只会被执行一次")
  3. def inner():
  4. import time
  5. start_time = time.time()
  6. func() # 执行被装饰的功能函数
  7. end_time = time.time()
  8. print("消耗的时间为:",end_time - start_time)
  9. return inner
  10. """九九加法表"""
  11. @get_time
  12. def print_add_table():
  13. for row in range(1,10):
  14. for col in range(1,row + 1):
  15. print(col, "+" ,row, "=", row+col, end="\t")
  16. print()
  17. print_add_table()
  18. print_add_table() # 无论调用几次被装饰的函数,装饰器的外层函数只会被执行一次
  19. """
  20. 为什么在装饰器中增加的新功能要在内层函数中添加?
  21. 因为在装饰器函数中 无论调用几次被装饰的函数,
  22. 装饰器外层函数的语句只会被执行一次,
  23. 第二次调用被装饰函数时不会执行装饰器外层函数语句
  24.  
  25. 当语法糖添加装饰器的时候,内部执行流程:
  26. 1。当使用语法糖给当前函数添加装饰器的时候[添加@时],装饰器特殊闭包的外层函数就已经被调用执行了
  27. 注意:外层函数的执行会有一个结果的返回,这个结果就是其中的内层函数
  28.  
  29. 2。为哪一个功能函数添加的装饰器 就使用该功能函数名字作为变量名,用于接收外层函数执行的返回结果(即内层函数)
  30. 此时 print_add_table = get_time(print_add_table) # 装饰器函数的参数 是 被装饰的函数
  31.  
  32. 3。现在此时 print_add_table 代表的是:装饰器特殊闭包中的内层函数;装饰器的参数func 代表的是被装饰的方法函数
  33. """
  • 装饰器的完善
  1. def get_time(func): # func的作用是明确给哪一个函数添加装饰器
  2. def inner(*values,**kwargs): # 带参数的功能函数添加装饰器 所传的值
  3. import time
  4. start_time = time.time()
  5. res = func(*values,**kwargs) # 执行被装饰的功能函数; values 传的参数
  6. end_time = time.time()
  7. print("消耗的时间为:",end_time - start_time)
  8. return res
  9. return inner
  10. """
  11. 计算两个数求和功能的执行时间。即带参数的功能函数 添加装饰器
  12. """
  13. @get_time
  14. def add(a,b):
  15. return a + b
  16. res = add(12,34)
  17. print(res)
  1. def one_oper(func):
  2. flag = False # 设置一个标记位。让装饰器只能返回一次结果
  3. def inner(*value,**kwargs):
  4. nonlocal flag
  5. if not flag:
  6. res = func(*value,**kwargs)
  7. flag = True
  8. return res
  9. return None
  10. return inner
  11. @one_oper
  12. def add(a,b):
  13. return a + b
  14.  
  15. res = add(12,34)
  16. print(res) # 结果:46
  17. res = add(34,12)
  18. print(res) # 结果:None

唯一输出

多个装饰器的执行流程

  • 代码
  1. """
  2. 一个装饰器可以装饰多个功能函数
  3. 一个功能函数可以被多个装饰器装饰 执行顺序采用就近原则执行[先执行距离被装饰函数近的]
  4. """
  5.  
  6. def outer1(func):
  7. print("这是第一个装饰器的外部函数")
  8. def inner1(*value,**kwargs):
  9. print("第一个装饰器的内部函数")
  10. res = func(*value,**kwargs)
  11. if res != None:
  12. return res
  13. return inner1
  14. def outer2(func):
  15. print("这是第二个装饰器的外部函数")
  16. def inner2(*value,**kwargs):
  17. print("第二个装饰器的内部函数")
  18. res = func(*value,**kwargs)
  19. if res != None:
  20. return res
  21. return inner2
  22. def outer3(func):
  23. print("这是第三个装饰器的外部函数")
  24. def inner3(*value,**kwargs):
  25. print("第三个装饰器的内部函数")
  26. res = func(*value,**kwargs)
  27. if res != None:
  28. return res
  29. return inner3
  30.  
  31. @outer1
  32. @outer2
  33. @outer3
  34. def show():
  35. print("演示添加多个装饰器的功能")
  36. # 被添加装饰器的功能函数只会被执行一次,而且是在最后才执行
  37. show()
  • 结果
  1. """
  2. 执行结果:
  3. 这是第三个装饰器的外部函数
  4. 这是第二个装饰器的外部函数
  5. 这是第一个装饰器的外部函数
  6. 第一个装饰器的内部函数
  7. 第二个装饰器的内部函数
  8. 第三个装饰器的内部函数
  9. 演示添加多个装饰器的功能
  10. 执行流程:
  11. @outer3 此时执行outer3外层函数
  12. ---> show = outer3(show)
  13. show === inner3此内部函数
  14. @outer2 此时执行outer2外层函数
  15. ---> show = outer2(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
  16. 此时型参func=show=innner3 当作实际参数传递给show ===> 即上一个inner3
  17. 这个时候被赋值的变量show又拥有了新值 inner2
  18. @outer1
  19. ---> show = outer1(show)[注意:执行上一个装饰器的时候,show已经被更改数据了]
  20. 此时型参func=show=innner2 当作实际参数传递给show ===> 即上一个inner2
  21. 这个时候被赋值的变量show又拥有了新值 inner1
  22.  
  23. 所有的装饰器走完之后,此时的show === inner1
  24. inner1执行完后,此时的show === inner2
  25. inner2执行完后,此时的show === inner3
  26. inner3执行完后,此时的show === 被装饰的功能函数
  27. """



装饰器-进阶

带参数的函数装饰器

  之前的例子中装饰器是不能接收参数的,其用法只能适用于一些简单的场景。不传参的装饰器,只能对被装饰函数执行固定逻辑。

  装饰器本身就是一个函数,不传参数的装饰器,只能对被装饰的函数执行固定的逻辑,会大大限制其能力。

  若要用两个内容大体一致,但是某些地方不同的逻辑,若不能传参数的话,就需要写两个装饰器。

  1. # 装饰器实现传参需要两层嵌套
  2. def say_hello(contry): # 外层函数的型参 接收 装饰器传来的实参
  3. def wrapper(func): # 中层函数的型参 接收 被添加装饰器函数
  4. def deco(*args, **kwargs): # 内层函数的型参 接收 被添加装饰器的带参数功能函数所传的实参
  5. if contry == "china":
  6. print("你好!")
  7. elif contry == "america":
  8. print('hello.')
  9. else:
  10. return
  11. func(*args, **kwargs) # 真正执行函数的地方。
  12. return deco
  13. return wrapper
  14.  
  15. @say_hello("america")
  16. def american():
  17. print("I am from America。")
  18. @say_hello("china")
  19. def chinese():
  20. print("我来自中国。")
  21.  
  22. american()
  23. chinese()
  24. """
  25. 结果:
  26. hello.
  27. I am from America。
  28. 你好!
  29. 我来自中国。
  30. """



装饰器-高级

不带参数的类装饰器

  1. """
  2. 基于类实现的装饰器,需要两个类方法来实现
  3. __init__ : 接收被装饰的函数
  4. __call__ : 实现装饰器的逻辑
  5. """
  6. class Text(object):
  7. def __init__(self, func): # 接收被装饰的函数
  8. self.func = func
  9. def __call__(self, *args, **kwargs): # 实现装饰器的逻辑
  10. print("正在被装饰的函数是:{func}".format(func=self.func.__name__))
  11. return self.func(*args, **kwargs)
  12. @Text
  13. def func(something):
  14. print("函数的参数是:{}!".format(something))
  15.  
  16. func("hello")
  17. """
  18. 结果:
  19. 正在被装饰的函数是:func
  20. 函数的参数是:hello!
  21. """

带有参数的类装饰器

  1. """
  2. 带参数的类装饰器
  3. __init__ :不再接收被装饰的函数,而是接收传入的参数。
  4. __call__ :接收被装饰函数,实现装饰逻辑。
  5. """
  6. class logger(object):
  7. def __init__(self, level='INFO'): # 接收装饰器传入的参数
  8. self.level = level
  9. def __call__(self, func): # 接受被装饰的函数
  10. def wrapper(*args, **kwargs):
  11. print("[{level}]: the function {func}() is running..."\
  12. .format(level=self.level, func=func.__name__))
  13. func(*args, **kwargs) # 真正执行被装饰的函数的语句
  14. return wrapper # 返回内层函数
  15.  
  16. @logger(level='WARNING')
  17. def say(something):
  18. print("say {}!".format(something))
  19. say("hello")
  20. @logger(level='DEBUG')
  21. def say(something):
  22. print("say {}!".format(something))
  23. say("hello")
  24. """
  25. 结果:
  26. [WARNING]: the function say() is running...
  27. say hello!
  28. [DEBUG]: the function say() is running...
  29. say hello!
  30. """



系统提供的装饰器

  Python语言本身提供的property内建装饰器,通常存在于类中的方法属性化。详情见 类的方法属性化 笔记整理

  1. """
  2. Python语言本身提供的property内建装饰器,通常存在于类中的方法属性化
  3. 可以将一个方法函数定义成一个属性,属性的值就是该函数return的内容。
  4. """
  5. # 用@property装饰过的函数,会将一个函数定义成一个属性,属性的值就是该函数return的内容。即方法属性化
  6. class Student(object):
  7. def __init__(self, name):
  8. self.name = name
  9. self.name = None
  10. @property
  11. def age(self):
  12. return self._age
  13. @age.setter
  14. def age(self, value):
  15. if not isinstance(value, int):
  16. raise ValueError('输入不合法:年龄必须为数值!')
  17. if not 0 < value < 100:
  18. raise ValueError('输入不合法:年龄范围必须0-100')
  19. self._age=value
  20. @age.deleter
  21. def age(self):
  22. del self._age
  23. XiaoMing = Student("小明")
  24.  
  25. # 设置属性
  26. XiaoMing.age = 25
  27. # 查询属性
  28. age = XiaoMing.age
  29. print(age)
  30. # 删除属性。删除后再去查询该属性就会报错
  31. del XiaoMing.age
  32. age = XiaoMing.age
  33. print(age)

python高级 之(二) --- 类装饰器的更多相关文章

  1. [b0019] python 归纳 (五)_类装饰器

    总结: 类装饰器, 本质是一个函数,输入一个类,返回一个类 Case 1 啥都没做 def deco(in_class): return in_class @deco class Cat: def _ ...

  2. python带参数的类装饰器

    # -*- coding: utf-8 -*- # author:baoshan # 带参数的类装饰器(和不带参数的类装饰器有很大的不同) # 类装饰器的实现,必须实现__call__和__init_ ...

  3. 接口测试基础——第7篇 简单的Python知识普及(二)之装饰器

    今天我们来学习python里面的“装饰器” 1.我在函数test运行前想先打印一些内容怎么办? def func(param):    print u"打印的内容"    para ...

  4. 【Python学习之二】装饰器

    装饰器 首先,给出装饰器的框架: def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return ...

  5. python 装饰器(五):装饰器实例(二)类装饰器(类装饰器装饰函数)

    回到装饰器上的概念上来,装饰器要求接受一个callable对象,并返回一个callable对象(不太严谨,详见后文). 那么用类来实现也是也可以的.我们可以让类的构造函数__init__()接受一个函 ...

  6. python基础整理4——面向对象装饰器惰性器及高级模块

    面向对象编程 面向过程:根据业务逻辑从上到下写代码 面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程 面向对象编程(Object Oriented Pro ...

  7. python 描述符 上下文管理协议 类装饰器 property metaclass

    1.描述符 #!/usr/bin/python env # coding=utf-8 # 数据描述符__get__ __set__ __delete__ ''' 描述符总结 描述符是可以实现大部分py ...

  8. Python进阶内容(二)--- 装饰器

    谈装饰器前,需要明白一件事,Python 中的函数和 Java.C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例如: def foo(): print(" ...

  9. 详解Python闭包,装饰器及类装饰器

    在项目开发中,总会遇到在原代码的基础上添加额外的功能模块,原有的代码也许是很久以前所写,为了添加新功能的代码块,您一般还得重新熟悉源代码,稍微搞清楚一点它的逻辑,这无疑是一件特别头疼的事情.今天我们介 ...

随机推荐

  1. Android Studio Terminal 常用命令

    一.Manifest merger failed with multiple errors, see logs 在android开发的时候,有时候会遇到这样的问题 Error:Execution fa ...

  2. Redis主从复制配置+哨兵模式

    架构设计: master:s0 slave:s1.s2 主机映射信息如下: 192.168.32.100 s0 192.168.32.101 s1 192.168.32.102 s2 1.安装Redi ...

  3. C语言1-2019秋第一周作业

    第一周作业 1.你对软件工程专业或者计算机科学与技术专业了解是怎样? 软件技术专业是计算机科学的一个分支,和软件科学与技术专业相比较,软件技术专业的侧重点在开发和技术的实际应用,而对软件开发的理论要求 ...

  4. PHP Swoole websocket协议实现

  5. BZOJ 2219 数论之神 (CRT推论+BSGS+原根指标)

    看了Po神的题解一下子就懂了A了! 不过Po神的代码出锅了-solve中"d-temp"并没有什么用QwQQwQQwQ-应该把模数除以p^temp次方才行. 来自BZOJ讨论板的h ...

  6. html2canvas 使用指南

    html2canvas(document.body).then(function(canvas) { document.body.appendChild(canvas); }); 属性参数: http ...

  7. Makefile简单编写实例

    介绍一下Makefile的简单编写例子. 编写Makefile的规则就是: 目标文件:依赖文件 (tab)编译规则 现在我有一个文件目录结构为: 解释一下这几个文件.首先我创建makefile目录,底 ...

  8. Spring Boot教程(三十九)使用MyBatis注解配置详解(2)

    增删改查 MyBatis针对不同的数据库操作分别提供了不同的注解来进行配置,在之前的示例中演示了@Insert,下面针对User表做一组最基本的增删改查作为示例: public interface U ...

  9. 分享代码到GitHub

    1.本地安装git 2.项目文件夹右键Git bash here,弹出git命令窗口 3.输入git init,使项目加入Git管理 4.输入git add .,将项目全部内容添加到git 5.输入“ ...

  10. Hadoop-2.7.5完全分布式搭建

    1.在虚拟机上安装Hadoop完全分布式准备工作 1)这里使用的是VMWare软件,在VMWare上安装一个CentOS6.5,并再克隆两个机器配置相关MAC地址,以及配置机器名 2)三台虚拟机配置好 ...