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)