zoukankan      html  css  js  c++  java
  • Python函数——装饰器

    前言

    给下面的函数加上运行时间

    def fib(n):
        a, b = 0, 1
        for i in range(n):
            print(b)
            a, b = b, a+b
        return b
    
    a = fib(50)

    修改一:改动函数

    import  time
    
    def fib(n):
        start = time.time()
        a, b = 0, 1
        for i in range(n):
            print(b)
            a, b = b, a+b
        print(time.time() - start)
        return b
    
    a = fib(50)

    修改二:不改动函数

    def fib(n):
        a, b = 0, 1
        for i in range(n):
            print(b)
            a, b = b, a+b
        return b
    
    import time
    def wrapper(func):
        def inner(*args, **kwargs):
            start = time.time()
            a = func(*args, **kwargs)
            print(time.time() - start)
            return a
        return inner  # 返回函数名
    
    fib = wrapper(fib)
    fib(50)

    忙活了这么半天,终于初具规模了!现在已经基本上完美了,唯一碍眼的那句话就是还要在做一次赋值调用。。。

    装饰器

    装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

    装饰器的本质:就是一个闭包函数

    满足开放封闭原则

    1.对扩展是开放的

    2.对修改是封闭的

    通过使用装饰器语法糖来解决这个问题!

    import time
    def wrapper(func):
        def inner(*args, **kwargs):
            start = time.time()
            a = func(*args, **kwargs)
            print(time.time() - start)
            return a
        return inner  # 返回函数名
    
    @wrapper  # fib = wrapper(fib)
    def fib(n):
        a, b = 0, 1
        for i in range(n):
            print(b)
            a, b = b, a+b
        return b
    
    fib(100)

    上面的装饰器已经非常完美了,但是正常我们情况下查看函数的一些信息的方法在此处都会失效

    import time
    def wrapper(func):
        def inner(*args, **kwargs):
            start = time.time()
            a = func(*args, **kwargs)
            print(time.time() - start)
            return a
        return inner  # 返回函数名
    
    @wrapper  # fib = wrapper(fib)
    def fib(n):
        a, b = 0, 1
        for i in range(n):
            print(b)
            a, b = b, a+b
        return b
    
    fib(100)
    print(fib.__doc__)
    print(fib.__name__)  # 返回 inner
    
    # 为了不让他们失效,我们还要在装饰器上加上一点来完善它:
    import time
    from functools import wraps
    def wrapper(func):
        @wraps(func)  # 加在最内层函数正上方
        def inner(*args, **kwargs):
            start = time.time()
            a = func(*args, **kwargs)
            print(time.time() - start)
            return a
        return inner  # 返回函数名
    
    @wrapper  # fib = wrapper(fib)
    def fib(n):
        a, b = 0, 1
        for i in range(n):
            print(b)
            a, b = b, a+b
        return b
    
    fib(100)
    print(fib.__doc__)
    print(fib.__name__)  # 返回 fib

    带参数的装饰器

    带参数的装饰器:就是给装饰器传参

            用处:就是当加了很多装饰器的时候,现在忽然又不想加装饰器了,想把装饰器给去掉了,但是那么多的代码,一个一个的去闲的麻烦,

                                          那么,我们可以利用带参数的装饰器去装饰它,这就他就像一个开关一样,要的时候就调用了,不用的时候就去掉了。给装饰器里面传个参数,

                                          那么那个语法糖也要带个括号。在语法糖的括号内传参。在这里,我们可以用三层嵌套,弄一个标识 为去标识。如下面的代码示例

    import time
    from functools import wraps
    flag = False
    
    def outer(flag):
        def wrapper(func):
            @wraps(func)  # 加在最内层函数正上方
            def inner(*args, **kwargs):
                start = time.time()
                if flag:
                    print('执行fib')
                    a = func(*args, **kwargs)
                else:
                    print('不执行fib')
                    a = ''
                print(time.time() - start)
                return a
            return inner  # 返回函数名
        return wrapper
    
    
    """
    带参数的装饰器执行过程
    fib = outer(False) (fib)
    --分解
    fib_decorator = outer(False)
    fib = fib_decorator(fib)
    --分解
    fib_decorator = outer(False)
    @fib_decorator
    def fib(n)
    所以,带参数的outer函数首先返回一个decorator函数,
    再让这个decorator函数接收fib并返回新函数:
    """
    @outer(True)
    def fib(n):
        a, b = 0, 1
        for i in range(n):
            print(b)
            a, b = b, a+b
        return b
    
    fib(100)
    print(fib.__doc__)
    print(fib.__name__)  # 返回 fib

    多个装饰器叠加使用

    def decorator_a(func):
        print('Get in decorator_a')
    
        def inner_a(*args, **kwargs):
            print('Get in inner_a')
            return func(*args, **kwargs)
        return inner_a
    
    
    def decorator_b(func):
        print('Get in decorator_b')
        
        def inner_b(*args, **kwargs):
            print('Get in inner_b')
            return func(*args, **kwargs)
        return inner_b
    
    
    @decorator_b
    @decorator_a
    def f(x):
        print('Get in f')
        return x * 2
    
    print(f(1))

    输出结果分析

    """
    装饰器执行顺序自下而上,内部函数调用顺序自上而下
    所以输出
    Get in decorator_a
    Get in decorator_b
    Get in inner_b
    Get in inner_a
    Get in f
    2
    分析
    当解释器执行下面这段代码时,实际上按照从下到上的顺序已经依次调用了 decorator_a 和 decorator_b ,
    这是会输出对应的 Get in decorator_a 和 Get in decorator_b 。 
    这时候 f 已经相当于 decorator_b 里的 inner_b 。但因为 f 并没有被调用,
    所以 inner_b 并没有调用,依次类推 inner_b 内部的 inner_a 也没有调用,
    所以 Get in inner_a 和 Get in inner_b 也不会被输出。
    然后最后一行当我们对 f 传入参数1进行调用时, inner_b 被调用了,
    它会先打印 Get in inner_b ,然后在 inner_b 内部调用了 inner_a 
    所以会再打印 Get in inner_a, 然后再 inner_a 内部调用的原来的 f,
    并且将结果作为最终的返回。这时候你该知道为什么输出结果会是那样,
    以及对装饰器执行顺序实际发生了什么有一定了解了吧。
    """

    装饰器使用举例

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Time    : 2018/4/4 22:38
    # @Author  : hyang
    # @Site    : 
    # @File    : 装饰器练习.py
    # @Software: PyCharm
    
    """
    三:编写装饰器,为函数加上认证的功能,即要求认证成功后才能执行函数
    四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),
    要求登录成功一次,后续的函数都无需再输入用户名和密码
    提示:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
    """
    
    user_data = {
        'user': None,
        'is_authenticated': False
    }
    
    
    def login_required(func):
        # 用户认证装饰器
        def inner(*args, **kwargs):
            if args[0].get('is_authenticated'):
                print('User is authenticated')
                ret = func(*args, **kwargs)
            else:
                exit("User is not authenticated !!!")
            return ret
        return inner
    
    
    def get_acct_info():
        """
        get acct_info from USER.TXT
        :return: 
        """
        with open(r'USER.TXT', 'r') as f:
            for line in f:
                user_dict = eval(line)
                USER_INFO_LI.append(user_dict)
    
    
    def auth_user():
        """
        认证用户
        :return: 
        """
        count = 0
        while count < 3:
            user = input('user >>> ').strip()
            pwd = input('pwd >>> ').strip()
            # print(USER_INFO_LI)
            user_flag = False
            pwd_flag = False
            for item in USER_INFO_LI:
                if user == item['name']:
                    user_flag = True
                    if user_flag:
                        if pwd == item['password']:
                            pwd_flag = True
            if user_flag and pwd_flag:
                # 用户认证成功
                user_data['user'] = user
                user_data['is_authenticated'] = True
                print('account login success')
                return user_data
            elif user_flag:
                # 密码不对
                print('pwd is error')
            else:
                # 用户名不存在
                print('user is not  exists')
            count += 1
        else:
            print("account [%s] too many login attempts" % user)
            exit()
    
    
    @login_required
    def print_info(p_data):
        print(p_data)
    
    
    @login_required
    def enter_x(p_data):
        print('enter_x', p_data['user'])
    
    
    @login_required
    def enter_y(p_data):
        print('enter_x', p_data['user'])
    
    
    def logout(p_data):
        print('logout', p_data['user'])
        p_data['is_authenticated'] = False
    
    
    def interactive(p_data):
        """
        interact with user
        :return:
           
        """
        menu = """
           ------- menu ---------
           1.  账户信息(功能已实现)
           2.  进入x(功能已实现)
           3.  进入y(功能已实现)
           4.  退出程序
           """
        print(menu)
        menu_dic = {
            '1': print_info,
            '2': enter_x,
            '3': enter_y,
            '4': logout
           }
        exit_flag = False
        while not exit_flag:
            user_option = input(">>:").strip()
            if user_option in menu_dic:
                menu_dic[user_option](p_data)
            else:
                print("Option does not exist!")
    
    
    if __name__ == '__main__':
        USER_INFO_LI = []
        get_acct_info()
        acc_data = auth_user()
        interactive(acc_data)
  • 相关阅读:
    httpd-2.2 配置及用法完全攻略
    在 Ubuntu 16.04 上安装 LEMP 环境之图文向导
    Zookeeper集群搭建
    smem – Linux 内存监视软件
    X.Org可能将失去它的域名x.org
    Docker 容器测试全探索
    Unix操作系统中UUCP知识详细讲解
    常用的Git Tips
    Unix操作系统中UUCP知识详细讲解
    将博客搬至CSDN
  • 原文地址:https://www.cnblogs.com/xiao-apple36/p/8714471.html
Copyright © 2011-2022 走看看