python全栈闯关--11-装饰器初识
1、装饰器形成
当不想修改原函数,未函数前后添加功能时,就可以使用装饰器,在函数前后增加功能。
装饰器的初始形成
import time def timer(f): def inner(): print("我是装饰器,我来装饰了!!!") start = time.time() f() end = time.time() print(start - end) return inner # 返回inner由于f实现了闭包,直接调用了程序 def func(): time.sleep(1) print("我是小鱼,要作作作。。。有本事来装饰我!") # 通过inner返回,在func前后进行了函数的功能的扩展 t = timer(func) t() # 虽然实现了功能,但是函数的调用名修改了 # 为了不改变调用名,可以赋值给func func = timer(func) func() # 通过装饰器,实现了不修改函数名的装饰
语法糖
在函数前面加,加上@函数名,就可以实现装饰器,叫做语法糖
def timer(f): def inner(): print("我是装饰器,我来装饰了!!!") start = time.time() f() end = time.time() print(start - end) return inner # 返回inner由于f实现了闭包,直接调用了程序 @timer def func(): time.sleep(1) print("我是小鱼,要作作作。。。有本事来装饰我!")
2、带参数和返回值的装饰器
import time def timer(f): def inner(a,b): # inner接收传入的参数 print("我是装饰器,我来装饰了!!!") start = time.time() ret = f(a,b) end = time.time() print(start - end) return ("我是装饰器的返回值!!!",start - end) # 在闭包内部,实现返回值 return inner # 返回inner由于f实现了闭包,直接调用了程序 @timer def func(a,b): time.sleep(1) print("我是小鱼,要作作作。。。有本事来装饰我!") print("a=%s,b=%s" % (a,b)) ret = func(1,2) # 参数相当于传递给inner print(ret)
执行顺序
3、传递任意参数的装饰器
通过*args和**kwargs传递任意参数
def warpper(f): def inner(*args, **kwargs): ret = f(*args, **kwargs) return ret return inner @warpper def func(*args, **kwargs): #*和**打散 print("我是被装饰的函数!") print(args) print(kwargs) func() # 传空参数 func(1,2,3,4,b=1, a=2) # 传任意参数 dic1 = {"a":1, "b":2} func(*(1,2,3,4),**dic1) # 打散传递
4、参数位置随感
个人观察结论:*是按顺序打散,形参位置:*args中,args打散后为1 2 3 4,所以args为(1,2,3,4)
def outer(*args): print(args) # (1, 2, 3, 4) print(*args) # 1 2 3 4 元祖被打散 def inner(*args): print('innner', args) inner(*args) # 打散传入inner(1,2,3,4) outer = outer(1,2,3,4)
5、warps
函数的__doc__和__name__可以查看函数的说明和名字。
装饰器后,函数名和名字都变为装饰器的内部函数
def warpeer(f): def inner(*args, **kwargs): ''' this is warpper inner :param args: :param kwargs: :return: ''' print("this is innser") ret = f(*args, ** kwargs); return ret return inner @warpeer def func(): print("this is func") print(func.__name__) print(func.__doc__)
加上wraps后,可以识别到函数自己的doc和name
from functools import wraps def warpeer(f): @wraps(f) def inner(*args, **kwargs): ''' this is warpper inner :param args: :param kwargs: :return: ''' print("this is innser") ret = f(*args, ** kwargs); return ret return inner @warpeer def func(): ''' this is func doc :return: ''' print("this is func") print(func.__name__) print(func.__doc__)
6、练习
# 1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件), # 要求登录成功一次,后续的函数都无需再输入用户名和密码 uname = "小鱼" upwd = "qwe" LOGTYPE = False def login(func): def in_login(): global LOGTYPE # print(LOGTYPE) if LOGTYPE: # print(LOGTYPE) func() return username = input("请输入用户名:") userword = input("请输入密码:") if username.strip() == uname and userword.strip() == upwd: ret = func() LOGTYPE = True return ret else: print("用户名或者密码错误!") pass return in_login @login def shoplist_add(): print("增加一件商品!") @login def shoplist_del(): print("删除一件商品!") shoplist_add() shoplist_del()
# 1.编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果 # 2.为题目1编写装饰器,实现缓存网页内容的功能: # 具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中 import os from urllib.request import urlopen as uop def catche(fun): def in_c(*args, **kwargs): file = "catche.txt" if not os.path.exists(file): with open(file, 'wb') as f: f.write("") if os.path.getsize(file) : with open(file, 'rb') as f: return f.read() else: ret = fun(*args, **kwargs) with open(file, 'wb') as f: f.write(b'****catche***' + ret) return ret return in_c @catche def get_url(url): ret = uop(url).read() return ret print(get_url("http://www.baidu.com")) print(get_url("http://www.baidu.com"))