• python函数装饰器


    装饰器

    一、无参装饰器

    1.什么是装饰器

    器是指工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为装饰器对象额外对象添加额外功能,因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加额外功能的。

    需要注意的是:

    • 装饰器本身其实是可以任意调用的对象
    • 被装饰的对象也可以是任意可调用的对象

    2.为什么要用装饰器

    如果我们已经上线 了一个项目,需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护应该遵循开发封闭原则,即软件于丹上线运行后,软件的维护对修改原码是封闭的,对扩展功能指的是开发的。

    装饰器的实现必须遵守俩大规则:

    1. 不修改被装饰对象的源代码。
    2. 不修改被装饰对象的调用方式。

    装饰器其实就是在遵守以上俩个原则的前提下被装饰对象添加新功能。

    怎么使用装饰器

    改变源代码

    import time
    
    
    def index():
        start = time.time()
        print('welcome to index')
        time.sleep(1)
        end = time.time()
        print('运行时间:', end - start)
    
    
    index()
    #输出:
    welcome to index
    运行时间: 1.0003464221954346
    
    

    编写重复代码

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    def f2():
        print('welcome to f2')
        time.sleep(1)
    
    
    start = time.time()
    index()
    end = time.time()
    print('运行时间:', end - start)
    
    start = time.time()
    f2()
    end = time.time()
    print('运行时间:', end - start)
    
    
    #输出:
    welcome to index
    运行时间: 1.0001671314239502
    welcome to f2
    运行时间: 1.0003397464752197
    

    第一种传参方式:改变调用方式

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    def time_count(func):
        start = time.time()
        func()
        end = time.time()
        print('运行时间:', end - start)
    
    
    time_count(index)
    
    #输出:
    welcome to index
    运行时间: 1.0005521774291992
    

    第二种传参方式:包给函数-外包

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    def time_count(func):
        def wrapper():
            start = time.time()
            func()
            end = time.time()
            print('运行时间:', end - start)
    
        return wrapper
    
    
    index = time_count(index)
    index()
     #输出:
       welcome to index
    运行时间: 1.0000648498535156
    

    4.完善装饰器

    上述的装饰器,最后调用index()的时候,其实是在调用wrapper(),因此如果原始的index()有返回值的时候,wrapper()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和wrapper()方法的返回值。

    import time
    
    
    def index():
        print('welcome to index')
        time.sleep(1)
        return 123
    
    
    def time_count(func):
        def wrapper():
            start = time.time()
            res = func()
            end = time.time()
            print('运行时间:', end - start)
    
            return res
    
        return wrapper
    
    
    index = time_count(index)
    res = index()
    print(res)
    #输出:
    welcome to index
    运行时间: 1.0007765293121338
    123
    

    如果原始的index()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于有wrapper()=index(),所以给wrapper()方法传参即可。

    import time
    
    
    def home(name):
        print(f'welcome {name} to home page')
        time.sleep(1)
        return name
    
    
    def time_count(func):
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f'{func} time is {end - start}')
            return res
    
        return wrapper
    
    
    home = time_count(home)
    res = home('chen')
    print('res:', res)
    
    #输出:
    welcome chen to home page
    <function home at 0x000002085D50B0D8> time is 1.0006473064422607
    res: chen
    

    5.装饰器 语法糖

    在被装饰函数正上方,并且是单独一行写上@装饰器名

    import time
    
    
    def time_count(func):
        def wrapper(*args, **kwargs):
            start = time.time()
            res = func(*args, **kwargs)
            end = time.time()
            print(f'{func} time is {end - start}')
            return res
    
        return wrapper
    
    
    @time_count
    def home(name):
        print(f'welcome {name} to home page')
        time.sleep(1)
        return name
    
    
    @time_count
    def index():
        print('welcome to index')
        time.sleep(1)
        return 123
    
    
    res = home('egon')
    print(f"res:{res}")
    
    #输出:
    welcome egon to home page
    <function home at 0x00000203FABAC948> time is 1.0005333423614502
    res:egon
    
    

    6.装饰器模板

    def func():
        def wrapper(*args,**kwargs):
            res = func(*args,**kwargs)
            return res
        return wrapper
    

    二、有参装饰器

    无参装饰器只套了两层,有参装饰器是套三层。

    #登陆注册的装饰器
    import time
    
    current_user = {'username': None}
    
    
    def login(func):
        def wrapper(*args, **kwargs):
            if current_user['username']:
                res = func(*args, **kwargs)
                return res
            user = input('username:').strip()
            pwd = input('password:').strip()
            if user == 'chen' and pwd == '123':
                print('login successful')
                current_user['username'] = user
                res = func(*args, **kwargs)
                return res
            else:
                print('user or password error')
    
        return wrapper
    
    
    @login
    def home(name):
        print(f'welcome {name} to home page')
        time.sleep(1)
        return name
    
    
    @login
    def index():
        print('welcome to index')
        time.sleep(1)
        return 123
    
    
    res = index()
    #输出:
    username:chen
    password:123
    login successful
    welcome to index
    

    对于上面的登陆注册,我们把用户登陆成功的信息写入内存文档中,但是在工业在,内存信息可以存在文本中,mysql中,mongodb中,但是我们只能让用户信息来自于file的用户可以认证,因此我们可以改写上述的装饰器。

    import time
    
    current_user = {'username': None}
    
    
    def login(func):
        def wrapper(*args, **kwargs):
            if current_user['username']:
                res = func(*args, **kwargs)
                return res
            user = input('username:').strip()
            pwd = input('password:').strip()
            engine = 'file'
            if engine == 'file':
                print('base of file')
                if user == 'chen' and pwd == '123':
                    print('login successful')
                    current_user['username'] = user
                    res = func(*args, **kwargs)
                    return res
                else:
                    print('user or password error')
            elif engine == 'mysql':
                print('base of mysql')
            elif engine == 'mongodb':
                print('base of mongodb')
            else:
                print('default')
    
        return wrapper
    
    
    @login
    def home(name):
        print(f'welcome {name} to home page')
        time.sleep(1)
    
    
    @login
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    res = index()
    
    #输出:
    username:chen
    password:123
    base of file
    login successful
    welcome to index
    

    1.三层闭包

    三层简单实例

    def f1(y):
        def f2():
            x = 1
    
            def f3():
                print('x:', x)
                print('y:', y)
    
            return f3
    
        return f2
    
    
    f2 = f1(2)
    f3 = f2()
    f3()
    #输出:
    x: 1
    y: 2
    

    现在需要修改原来需求,需要判断用户动态的获取用户密码的方式,如果是file类型的我们则让用户进行认证,因此我们需要使用,有参装饰器。

    import time
    
    current_user = {'username': None}
    
    
    def auth(engine='file'):
        def login(func):
            def wrapper(*args, **kwargs):
                if current_user['username']:
                    res = func(*args, **kwargs)
                    return res
                user = input('username:').strip()
                pwd = input('password:').strip()
                if engine == 'file':
                    print('base of file')
                    if user == 'chen' and pwd == '123':
                        print('登陆成功')
                        current_user['username'] = user
                        res = func(*args, **kwargs)
                        return res
                    else:
                        print('用户密码或者用户名错误')
                elif engine == 'mysql':
                    print('base of mysql')
                elif engine == 'mongodb':
                    print('base of mongodb')
                else:
                    print('please base of file')
    
            return wrapper
    
        return login
    
    
    @auth(engine='mysql')
    def home(name):
        print(f'welcome {name} to home page')
        time.sleep(1)
    
    
    @auth(engine='file')
    def index():
        print('welcome to index')
        time.sleep(1)
    
    
    res = index()
    #输出:
    username:chen
    password:123
    base of file
    登陆成功
    welcome to index
    

    由于倆层的装饰器,参数必须得固定定位func,但是三层的装饰器解除了这个限制,我们不仅仅可以使用上述单个参数的三层装饰器,多个参数的只需要在三层装饰器中多家几个参数即可,也就是说装饰器三层即可,多加无用。

    俩个装饰器的叠加

    def outter1(func):  # func = wrapper2
        def wrapper1(*args, **kwargs):  # wrapper是未来要运行的函数
            print('------------')
            res = func(*args, **kwargs)  # func是被装饰的函数  # wrapper2
            print('------------')
            return res
    
        return wrapper1
    
    
    def outter2(func):  # func = index
        def wrapper2(*args, **kwargs):  # wrapper是未来要运行的函数
            print('11111111111111')
            res = func(*args, **kwargs)  # func是被装饰的函数  # index()
            print('11111111111111')
            return res
    
        return wrapper2
    
    
    # @outter1  # index = outter1(index)
    # @outter2  # index = outter2(index)  # 先运行最下面的装饰器
    # # index
    def index():
        print('index')
    
    
    # index重新定义的index = outter2(index 真正的index)
    index = outter2(index)  # index = wrapper2
    # index再一次重新定义的index = outter1(index重新定义的index,即wrapper2)
    index = outter1(index)  # index  = wrapper1
    index()  # wrapper1()
    
    #输出:
    ------------
    11111111111111
    index
    11111111111111
    ------------
    

    重要,装饰器的模板

    双层装饰器

    def outter(func):
        def wrapper(*args,**kwargs):
            #加功能
            res = func(*args,**kwargs)
            return res
        return wrapper
    

    三层装饰器模板

    主要是用来给 倆层装饰器添加参数

    def sanceng(engine):
        def outter(func):
            def wrapper(*args, **kwargs):
                # 加功能
                print(engine)
                res = func(*args, **kwargs)
                return res
    
            return wrapper
    
        return outter
    
    
    @sanceng('file')
    def shopping():
        print('shopping')
    
    
    shopping()
    #输出:file
    shopping
    
  • 相关阅读:
    java小学生四则运算带面板版 但我不知道为什么同类变量却进不了动作监听中去
    超实用 2 ArrayList链表之 员工工资管理系统
    超实用 1 ArrayList 链表
    我的JAVA运算符理解
    小学生四则运算
    一、二周冲刺总结
    阅读《构建之法》第8,9,10章
    第一次Sprint计划
    作业6
    5.2
  • 原文地址:https://www.cnblogs.com/SkyOceanchen/p/11342138.html
Copyright © 2020-2023  润新知