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)
原文地址:https://www.cnblogs.com/xiao-apple36/p/8714471.html