zoukankan      html  css  js  c++  java
  • python高级 之(二) --- 类装饰器

     装饰器-初级

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

    • 简介
     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="	")
    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="	")
    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="	")
    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="	")
    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 
    16 res = add(12,34)
    17 print(res) # 结果:46
    18 res = add(34,12)
    19 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)
    生如逆旅 一苇以航
  • 相关阅读:
    窗体设计器出不来
    maven ...../.m2/settings.xml
    myeclipse.ini
    人民币大小写
    驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立 安全连接。错误:
    写了一个浏览器插件
    用Excel计算加权平均分和GPA
    cfree使用cygwin编译程序出现计算机丢失cygwin1.dll解决办法
    apt-cyg update --2017-02-17 07:57:24-- http://mirrors.163.com/cygwin//x86_64/setup.bz2 正在解析主机 mirrors.163.com... 123.58.173.185, 123.58.173.186 正在连接 mirrors.163.com|123.58.173.185|:80... 已连接。 已发出 HTT
    生产者消费者问题
  • 原文地址:https://www.cnblogs.com/TMMM/p/11395710.html
Copyright © 2011-2022 走看看