• Python装饰器


    前言

    装饰器顾名思义,它是对原来的函数进行修饰增加新的功能,但是它又不修改原来的函数。

    定义:

    • 传入的参数和返回的都是函数,则这个函数就是装饰器。
    • 装饰器在不修改被装饰的函数前提下能为被装饰函数提供额外的附加功能。

    场景

    日常开发中,可以用到装饰器的场景包括但不限于:

    ◆引入日志
    ◆函数执行时间统计
    ◆执行函数前预备处理
    ◆执行函数后清理功能
    ◆权限校验等场景
    ◆缓存

    示例说明

    装饰器的引入

    def now():
        print('i am chinese')
    now()
    

    如果我们想在这个函数执行之前打印Hello!,在函数执行结束打印GoodBye!在没使用装饰器时,方法如下:

    def now():
        print('Hello!')
        print('i am chinese')
        print('GoodBye!')
    now()
    

    这样虽然解决问题,但是如果要修改的函数较多,工作量会大量增加,并且后续维护也是个麻烦事。于是我们的装饰器的优势就体现出来。

    一个装饰器装饰多个函数

    #创建一个函数,传入的参数和返回的都是函数,则这个函数就是装饰器
    
    def new_now(fun):
        def warrp():
            print('Hello!')
            fun()
            print('GoodBye!')       
        return warrp
    
    def now():
        print('i am chinese')
    
    def now2():
        print('i am Janpese')
    f = new_now(now)
    f()
    
    f1 = new_now(now2)
    f1()
    

    装饰器通用的调用方法是在装饰函数前加一个@符号并放在被装饰函数前面,@new_now它相当与执行了f = new_now(now):

    def new_now(fun):
        def warrp():
            print('Hello!')
            fun()
            print('GoodBye!')       
        return warrp
    @new_now
    def now():
        print('i am chinese')
    @new_now
    def now2():
        print('i am Janpese')
    now()
    now2()
    

    多个装饰器装饰一个函数

    def new_test(fun):
        def warrp():
            print('你好啊!')
            f = fun()
            return f
        return warrp
    
    def new_now(fun):
        def warrp():
            print('Hello!')
            f = fun()   
            return f
        return warrp
    
    @new_test   
    @new_now
    def now():
        print('i am chinese')
        
    now()
    
    

    多个装饰器装饰一个函数时主要注意执行的顺序。

    def new_test(fun):
        def warrp():
            print('你好啊!')
            f = fun()
            print('再见!')
            return f
        return warrp
    
    def new_now(fun):
        def warrp():
            print('Hello!')
            f = fun()   
            print('GoodBye!')
            return f
        return warrp
    
    @new_test   
    @new_now
    def now():
        print('i am chinese')
    
    now()
    

    看到上面的示例是否觉得顺序很好理解,我们在来看看下面的示例,可能和你的理解思路会有不少冲突哦。

    def new_test(fun):
        print('你好啊!')
        def warrp():
            print('再见!')
            f = fun()
            print('下次再见!')
            return f
        return warrp
    
    def new_now(fun):
        print('Hello!')
        def warrp():
            print('GoodBye!')
            f = fun()   
            print('see you late!')
            return f
        return warrp
    
    @new_test   
    @new_now
    def now():
        print('i am chinese')
    
    now()
    

    def new_test(fun):
        print('你好啊!')
        def warrp():
            print('再见!')
            f = fun()
            print('下次再见!')
            return f
        return warrp
    
    def new_now(fun):
        print('Hello!')
        def warrp():
            print('GoodBye!')
            f = fun()   
            print('see you late!')
            return f
        return warrp
    
    # @new_test   
    # @new_now
    def now():
        print('i am chinese')
    
    now = new_test(new_now(now))
    now()
    


    它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器.
    先执行里面的new_now函数,打印'Hello',然后执行外部的new_test函数打印'你好',再执行new_test内部的warrp函数打印'再见',然后执行new_now内层的warrp函数打印'GoodBye',再执行被装饰的now函数打印'i am chinese',然后从里往外执行,依次打印'see you late','下一再见!'

    被装饰函数+参数

    #装饰器统计程序运行时间
    from datetime import datetime
    from time import sleep
    
    def run_time(fun):
        """
            将其他函数当作参数传入,计算这个函数的执行时间
        """
        def wrapper(*arg,**args):
            init_time = datetime.now()     
            f = fun(*arg,**args)
            now_time = datetime.now()
            print('程序执行时间:',str((now_time - init_time).seconds),'秒')
            print('程序执行结果:')
            return f
        return wrapper
    
    @run_time
    def run(x):
        sleep(1)    
        print(x,'程序执行完成')
        return x
    
    @run_time
    def run2(x,y):
        sleep(2)
        print(x ,'+', y,'程序执行完成')
        return x + y
    print(run(1))
    print(run2(1,1))
    
    

    装饰器+参数

    import logging
    logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s',
                        level=logging.DEBUG)
    
    def use_logging(level):
       def decorator(func):
           def wrapper(*args, **kwargs):
               if level == "warn":
                   logging.warning("%s is running by warning" % func.__name__)
               elif level == "info":
                   logging.info("%s is running by info" % func.__name__)
               return func(*args,**kwargs)
           return wrapper
    
       return decorator
    
    @use_logging(level="warn")
    def fun_a(*args, **kwargs):
       print("i am %s %s" % (args,kwargs))
    
    @use_logging(level="info")
    def fun_b(*args, **kwargs):
       print("i am %s %s" % (args,kwargs))
    
    fun_a(18,20,name="张三")
    fun_b(20,name="李四",age=18)
    

    类装饰器

    装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。

    #类装饰器计算时间
    import time
    class Show_time():
        def __init__(self,fun):
            self.fun = fun
        def __call__(self):
            start_time = time.time()
            self.fun()
            end_time = time.time()
            show_time = end_time - start_time
            print('运行时间:%.3f秒'%show_time)
    @Show_time
    def fun_a():
        time.sleep(1)
        print('fun_a is running')
    
    @Show_time
    def fun_b():
        time.sleep(2)
        print('fun_b is running')
    fun_a()
    fun_b()
    

    @property装饰器示例

    Python中的@property是python的一种装饰器,是用来修饰方法的。
    作用:我们可以使用@property装饰器来创建只读属性,@property装饰器会将方法转换为相同名称的只读属性,可以与所定义的属性配合使用,这样可以防止属性被修改。

    #property装饰器
    #方法加入@property后,这个方法相当于一个属性,这个属性可以让用户进行使用,而且用户有没办法随意修改
    class DataSet(object):
        def __init__(self):
            self.__name = "张三"
            self.__age = 18 #定义属性的名称
        @property
        def name(self): 
            return self.__name
        @property
        def age(self):
            return self.__age
    test_object = DataSet()
    print(test_object.name) # 加了@property后,可以用调用属性的形式来调用方法,后面不需要加()。
    print(test_object.age)
    

  • 相关阅读:
    通过字符串调用函数
    First,FirstOrDefault和Single,SingleOrDefault 的区别
    asp循环例子
    将il文件和资源文件生成dll工具
    C# 修改资源文件工具ResourceNet4
    北京ip
    实验1、Mininet 源码安装和可视化拓扑工具
    实验2:Mininet 实验——拓扑的命令脚本生成
    ajax post data 获取不到数据,注意 contenttype的设置 、post/get
    调试提示:当前不会命中断点
  • 原文地址:https://www.cnblogs.com/huny/p/13956953.html
Copyright © 2020-2023  润新知