高阶函数的两种情况:
1、
把一个函数名当成实参传给另外一个函数(在不修改被装饰函数源代码的情况下,为其添加功能)
import time def bar(): time.sleep(2) print('in the bar') def test1(fun_name): t1 = time.time() fun_name() print('run time:%s'%(time.time()-t1)) test1(bar)
2、
返回值中包含函数名(不修改调用方式)
import time def bar(): time.sleep(2) print('in the bar') def test1(fun_name): return fun_name bar = test1(bar) bar()
高阶函数+嵌套函数则得到装饰器
import time def timer(fun_name): def deco(): start_time = time.time() fun_name() end_time = time.time() print('running time:%s' % (end_time - start_time)) return deco def bar(): time.sleep(2) print('in the bar') bar = timer(bar) bar()
但我们一般把装饰器在被装饰函数前面定义,并且用如下的写法代替:
import time def timer(fun_name): def deco(): start_time = time.time() fun_name() # 在这里调用是bar()这个函数 end_time = time.time() print('running time:%s' % (end_time - start_time)) return deco @timer # bar = timer(bar) 这步其实是调用了timer()这个函数,把bar这个函数进行重新定义 def bar(): time.sleep(2) print('in the bar') bar() # 在调用bar的时候,其实调用的是deco()这个函数
可以理解为,先用第一种高阶函数加上想要的功能,再重新覆盖定义这个函数,再用函数嵌套,就得到了我们想要的装饰器的效果
如果有参数:
import time def timer(fun_name): def deco(*args,**kwargs): start_time = time.time() fun_name(*args,**kwargs) end_time = time.time() print('running time:%s' % (end_time - start_time)) return deco @timer # bar = timer(bar) def bar(): time.sleep(2) print('in the bar') @timer def bar2(name,age): print(name,age) bar() bar2('simon',22)
终极版:原函数带有返回值,装饰器本身带参数
__author__ = "Alex Li" import time user, passwd = 'alex', 'abc123' def auth(auth_type): print("auth func:", auth_type) def outer_wrapper(func): def wrapper(*args, **kwargs): print("wrapper func args:", *args, **kwargs) if auth_type == "local": username = input("Username:").strip() password = input("Password:").strip() if user == username and passwd == password: print(" 33[32;1mUser has passed authentication 33[0m") res = func(*args, **kwargs) # from home print("---after authenticaion ") return res else: exit(" 33[31;1mInvalid username or password 33[0m") elif auth_type == "ldap": print("搞毛线ldap,不会。。。。") return wrapper return outer_wrapper def index(): print("welcome to index page") @auth(auth_type="local") # home = wrapper() def home(): print("welcome to home page") return "from home" @auth(auth_type="ldap") def bbs(): print("welcome to bbs page") index() print(home()) # wrapper() bbs()
几个装饰器的小练习和写的答案
一:编写3个函数,每个函数执行的时间是不一样的,
提示:可以使用time.sleep(2),让程序sleep 2s或更多,
二:编写装饰器,为每个函数加上统计运行时间的功能
提示:在函数开始执行时加上start=time.time()就可纪录当前执行的时间戳,函数执行结束后在time.time() - start就可以拿到执行所用时间
三:编写装饰器,为函数加上认证的功能,即要求认证成功后才能执行函数
四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
提示:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
答案:
# -*- coding: utf-8 -*- import time, os if not os.path.exists('login_for Decorator.txt'): with open('login_for Decorator.txt', 'w', encoding='utf-8')as f: f.write(str({'name': 'simon', 'password': '123'})) with open('login_for Decorator.txt', 'r', encoding='utf-8')as f2: dict_list = eval(f2.read()) name, password = dict_list['name'], dict_list['password'] login_status = 0 # 验证是否登录,一次登录,后面不再需要登录则可以运行 def login(func): def deco(*args, **kwargs): global login_status if not login_status: if_wrong = 1 # 默认密码是错误的 while if_wrong: # 如果输入错误,就一直循环输入 _name = input('your name :').strip() _password = input('type your password:').strip() if _name == name and _password == password: print('welcome %s login' % _name) login_status = 1 if_wrong = 0 func() else: pass # print('不应该出现这个') else: func() return deco # 此装饰器的作用是打印函数运行时间 def timer(func): def deco(*args, **kwargs): t1 = time.time() func() print('running time:%s' % (time.time() - t1)) return deco @login def fun1(): time.sleep(3) print('here is fun1') @login def fun2(): time.sleep(3) print('here is fun2') @login def fun3(): time.sleep(3) print('here is fun3') fun1() fun2() fun3()