zoukankan      html  css  js  c++  java
  • 15 Python学习之装饰器

    装饰器

    装饰器:在不改变源函数的代码及调用方式的前提下,为其增加新的功能,装饰器就是一个函数,他的本质是闭包

    装饰器开放封闭的原则:

    开放:对代码的扩展开放

    封闭:对源码的修改是封闭的

    被装饰函数无参数,无返回值

    例1:

    import time
    
    def timer(func):
        def inner():
            start_time = time.time()
            func()
            end_time = time.time()
            print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
        return inner            # 闭包,返回内层函数的引用
    
    # show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info = timer(show_info)的简写,官方叫做语法糖
    @timer
    def show_info():
        print(f"我的姓名是:张三")
        time.sleep(2)
    
    
    show_info()
    

    运行结果:

    我的姓名是:张三
    装饰器打印执行时间为:2.00051

    被装饰函数有返回值

    被装饰函数有返回值,那么在设计装饰器的时候,就应该有变量进行接收,然后再将该返回值返回给调用者

    例1:

    import time
    
    def timer(func):
        def inner():
            start_time = time.time()
            inner_ret = func()              # 调用原函数并接受返回值
            end_time = time.time()
            print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
            return inner_ret                # 将调用原函数的返回值返回给调用者
        return inner            # 闭包,返回内层函数的引用
    
    
    @timer          # show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info = timer(show_info)的简写,官方叫做语法糖
    def show_info():
        print(f"我的姓名是:张三")
        time.sleep(2)
        return 'show_info函数的返回值'
    
    
    ret = show_info()           # ret接受的是inner的返回值inner_ret
    print(ret)
    

    我的姓名是:张三
    装饰器打印执行时间为:2.00168
    show_info函数的返回值

    被装饰函数有参数

    被装饰函数有参数,由于实际调用的时候,掉的是闭包中的内部函数,所以内部函数相应的也要设计成跟原函数一样的带参数

    例1:

    import time
    
    def timer(func):
        def inner(name):               # 设计时需要添加参数
            start_time = time.time()
            inner_ret = func(name)              # 调用原函数indxe,传入参数并接受返回值
            end_time = time.time()
            print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
            return inner_ret                # 将调用原函数的返回值返回给调用者
        return inner            # 闭包,返回内层函数的引用
    
    
    @timer          # show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info= timer(show_info)的简写,官方叫做语法糖
    def show_info(name):
        print(f"我的姓名是:{name}")
        time.sleep(2)
        return 'show_info函数的返回值'
    
    
    ret = show_info('张三')       # 相当于ret = inner('张三')
    print(ret)
    

    我的姓名是:张三
    装饰器打印执行时间为:2.00031
    show_info函数的返回值

    标准装饰器

    由于被装饰函数的参数个数不确定,所以我们在设计装饰器时,内部函数的参数个数就无法确定,因此要设计成不定长的形式

    例1:

    import time
    
    def timer(func):
        def inner(*args, **kwargs):               # 设计时需要添加参数
            start_time = time.time()
            inner_ret = func(*args, **kwargs)     # 调用原函数indxe,传入参数并接受返回值
            end_time = time.time()
            print(f'装饰器打印执行时间为:{round(end_time - start_time, 5)}')
            return inner_ret                # 将调用原函数的返回值返回给调用者
        return inner            # 闭包,返回内层函数的引用
    
    
    @timer          # show_info = timer(show_info)第一个show_info是一个新的变量,第二个show_info的函数名,@timer是show_info= timer(show_info)的简写,官方叫做语法糖
    def show_info(name, age):
        print(f"我的姓名是:{name}, 今年{age}岁")
        time.sleep(2)
        return 'show_info函数的返回值'
    
    
    ret = show_info('张三', 25)
    print(ret)
    

    我的姓名是:张三, 今年25岁
    装饰器打印执行时间为:2.0017
    show_info函数的返回值

    特别注意

    在标准装饰器中,def inner(*args, **kwargs): 中的 * 在函数 定义 的时候是将传入函数的参数聚合成一个元素,在 调用函数 inner_ret = func(*args, **kwargs) 的时候 * 的作用是将一个可迭代的对象进行打散,即将变量 args或kwargs 打散,拆分成一个个要传入的实参

    标准装饰器模板:

    def decorator(real_func_name):
        def inner(*args, **kwwargs):
            '''调用装饰器访问函数前要执行的操作'''
            ret = real_func_name(*args, **kwwargs)
             '''调用装饰器访问函数后要执行的操作'''
            return ret
       	return inner
    

    装饰器的应用

    装饰器一般用于登录验证和日志

    例1:

    def login():
        name = input('请输入用户名:')
        pwd = input('请输入密码:')
        login_status['name'] = name
        if pwd == '123456':
            login_status['status'] = True
            return 1
        else:
            return 0
    
    
    login_status = {
        'name': None,
        'status': False
    }
    
    
    def decorator(func_name):
        def inner(*args, **kwargs):
            if login_status['status']:
                ret = func_name(*args, **kwargs)
                return ret
            else:
                ret = login()
                if ret:
                    ret = func_name()
                    return ret
                else:
                    print('登录失败')
    
        return inner
    
    
    @decorator
    def index():
        print("index页面")
    
    @decorator
    def logger():
        print("logger页面")
    
    # 只有登录成功后才会执行函数,并打印信息
    index()
    logger()
    
    

    请输入用户名:张三
    请输入密码:123456
    index页面
    logger页面

    带参数的装饰器

    如果给不同的函数在调用装饰器的时候,给出不同的日志记录文件,那么就需要在装饰函数的时候,传入一个装饰器路径参数

    例1:

    import datetime
    import os
    
    # 带参数的装饰器
    def logger(file):
        def outer(func):
            def inner(*args, **kwargs):
                # 如果目录不存在创建目录,并修改目录权限
                dir_path = '/projects/logger/'
                if not os.path.exists(dir_path):
                    os.makedirs(dir_path)
                    os.chmod(dir_path, mode=0o777)
                    print('文件夹不存在已自动创建')
                    
                # 执行被装饰的函数
                ret = func(*args, **kwargs)
                
                # 将创建文件信息写入文件
                with open(os.path.join(dir_path, file), mode='a+',encoding='utf-8') as f:
                    dt_ms = datetime.datetime.now()
                    f.write(f'于 {dt_ms} 调用了 {func.__name__}
    ')
                
                return ret
    
            return inner
    
        return outer
    
    # 带参数的装饰器
    @logger('goods.log')
    def goods():
        print('购买商品')
    
    # 带参数的装饰器
    @logger('login.log')
    def login():
        print('登录')
    
    # 带参数的装饰器
    @logger('order.log')
    def order():
        print('下单')
    
    
    if __name__ == '__main__':
        for i in range(5):
            goods()
            login()
            order()
    

    运行结果:

    [root@localhost logger]# cat login.log
    于 2020-08-18 11:56:44.415420 调用了 login
    于 2020-08-18 11:56:44.415650 调用了 login
    于 2020-08-18 11:56:44.415873 调用了 login
    于 2020-08-18 11:56:44.416067 调用了 login
    于 2020-08-18 11:56:44.416260 调用了 login

    [root@localhost logger]# cat order.log
    于 2020-08-18 11:56:44.415511 调用了 order
    于 2020-08-18 11:56:44.415745 调用了 order
    于 2020-08-18 11:56:44.415944 调用了 order
    于 2020-08-18 11:56:44.416126 调用了 order
    于 2020-08-18 11:56:44.416364 调用了 order

    [root@localhost logger]# cat goods.log
    于 2020-08-18 11:56:44.415233 调用了 goods
    于 2020-08-18 11:56:44.415586 调用了 goods
    于 2020-08-18 11:56:44.415813 调用了 goods
    于 2020-08-18 11:56:44.416007 调用了 goods
    于 2020-08-18 11:56:44.416199 调用了 goods

  • 相关阅读:
    作业三:模拟 mysql 进行增删改查
    作业一:购物车程序
    作业一:登陆接口
    python输出带颜色详解
    浅谈面向对象开发原则:高内聚,低耦合
    DIV+CSS命名规范
    ModelSim 仿真教程
    [HDL]除法器的设计与仿真(转)
    ModelSim中Altera仿真库的添加(转)
    PictureBox中的Image对象(或者图片)转存到数据库
  • 原文地址:https://www.cnblogs.com/zcf-blog/p/13447515.html
Copyright © 2011-2022 走看看