闭包函数与装饰器
一、什么是闭包
闭包:闭是封闭(函数内部函数),包是包含(函数内部函数对外部作用域而非全局作用域的变量的引用)。闭包指的是:函数内部函数对外部作用域的引用。
x = 111 # 全局作用域的变量def outer(): y = 222 # 局部作用域的变量 def inner(): print(x,y) # 引用了全局和局部作用域的变量 return inner
二、给函数体传值的的两种方式
1、传参
def save_file(filename,content): # 通过参数给函数体代码传值 with open(filename,'a',encoding='utf-8') as fw: fw.write(content) return
2、闭包
def deco(): x = 1 # x的值写死了,无法修改 y = 10 # y的值写死了,无法修改 def my_max(): if x > y: # 函数内部函数x对外部作用域x=1的引用,就是闭包 return x return y return my_maxres = deco()print(res())
# 闭包函数进阶def deco(x,y): # x,y的值在调用时可以改变。 def my_max(): if x > y: return x return y return my_maxres = deco(4,9) # 优点一次传参,多次调用。print(res())print(res())
三、闭包函数的应用
闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,先使用自己外部包裹的作用域。
闭包函数,可以是一次传参,多次调用。
应用领域:延迟计算(原来我们是传参,现在我们是包起来)、爬虫领域。
# 使用函数传参实现import requestsurl = 'https://www.baidu.com'url2 = '....'def my_get(url): response = requests.get(url) print(response.text)my_get(url) # 需要时,每次都要定义网址。如果需要爬取的网址比较多,会特别麻烦my_get(url) my_get(url)
# 使用闭包函数实现def deco(url): def my_get(): response = requests.get(url) print(response.text) return my_getmy_jd = deco('http://www.jd.com') # 一次定义,多次调用调用my_jd() # 多次调用闭包函数my_jd()my_jd()my_baidu = deco('https://www.baidu.com')my_baidu()my_baidu()my_baidu()my_baidu()
装饰器
什么是装饰器?
器就是一个工具,而程序中的函数就是具备某一功能的工具,所以装饰器就是给被装饰对象添加新的功能。因此定义装饰器就是定义一个函数,只不过该函数的功能是用来为其他函数添加新的功能。
需要注意的是:
- 装饰器本身其实是任意可调用的对象
- 被装饰的对象也是任意可调用的对象
为什么要用装饰器?
如果我们上线了一个项目,我们需要修改某一个方法,但是我们不想修改方法的使用方法,这个时候可以使用装饰器。因为软件的维护必须遵循开封封闭原则,即软件一旦上线运行后,软件的维护对修改源代码是封闭的,对扩展功能值的是开放的。
装饰器的实现必须遵循两大原则:
1.不修改被装饰对象的源代码
2.不修改被装饰对象的调用方式
怎么用装饰器?
# 提前了解知识# 导入时间模块import timeprint(time.time()) # 时间戳 当前时间距离1970-1-1 00:00:00相差的秒数# 1970-1-1 00:00:00是Unix诞生元年time.sleep(1) # 睡眠3秒,程序暂停3秒print('FBI warning!')
要实现统计index函数执行的时间
# 源代码import timedef index(): time.sleep(3) print('澳门最大赌场上线了 性感MM在线发牌')
# 通过修改源代码实现,实现统计index函数执行的时间import timedef index(): time.sleep(3) print('澳门最大赌场上线了 性感MM在线发牌')start = time.time()index()end = time.time()print(f'index run time {end-start}')
# 通过改变源函数的调用方式实现import timedef index(): time.sleep(3) print('澳门最大赌场上线了 性感MM在线发牌') def get_time(func): # func = index start = time.time() func() # index() end = time.time() print(f'index run time {end-start}')get_time(index) # 通过函数传参的方法实现了,但是改变了源函数的调用方式
# 通过闭包函数实现import timedef index(): time.sleep(3) print('澳门最大赌场上线了 性感MM在线发牌')def outter(func): # func = 原始的index def get_time(): # 无参装饰器 start = time.time() func() end = time.time() print(f'index run time {end-start}') return get_timeindex = outter(index) # get_timeindex() # func()= index()
完善装饰器
上述的装饰器,最后调用index()的时候,其实是在调用get_time(),因此如果原始的index()有返回值的时候,get_time()函数的返回值应该和index()的返回值相同,也就是说,我们需要同步原始的index()和get_time()方法的返回值。
import timedef index(): time.sleep(3) print('澳门最大赌场上线了 性感MM在线发牌') return 123def outter(func): def get_time(): start = time.time() res = func() end = time.time() print(f'index run time {end-start}') return res return get_timeindex = outter(index) # get_timeres = index #.get_time at 0x079D2DB0>print(res)# index()res2 = index()print(res2) # 123澳门最大赌场上线了 性感MM在线发牌index run time 3.000171661376953
如果我们想要该装饰器取装饰一个有参函数
def login(name): time.sleep(1) print(f'{name} is sb') return 'login'
函数参数的问题:
无参函数和有参函数都可以直接使用,函数可以以接收任意数量的参数。
如果原始的login()方法需要传参,那么我们之前的装饰器是无法实现该功能的,由于login = outer(login),实际上outer(login)是等于get_time的,所以,要想login()传参,只需要在get_time()中传参即可。get_time(*args,**kwargs),实际上就是将原始的login(name)中的位置实参用元组接收,关键字实参用字典接收,并赋值给args,kwargs,fun(*args,**kwargs)将赋值的args,kwargs即(元组和字典)打散成位置形参传给login的形参。
def index(): time.sleep(3) print('澳门最大赌场上线了 性感MM在线发牌') return 'index'# res1 = index()def login(name): time.sleep(1) print(f'{name} is sb') return 'login'# res = login('egon')def outter(func): def get_time(*args,**kwargs): start = time.time() res = func(*args,**kwargs) end = time.time() print(f'func run time {end-start}') return res return get_timelogin = outter(login) # login = get_timeres = login('egon') # login('egon') = get_time('egon')print(res) # res = 'login'egon is sbfunc run time 1.0000572204589844login
语法糖
import timedef outter(func): def get_time(*args,**kwargs): start = time.time() res = func(*args,**kwargs) end = time.time() print(f'func run time {end-start}') return res return get_time@outter # 等同于 index = outter(index) # index = get_timedef index(): time.sleep(3) print('澳门最大赌场上线了 性感MM在线发牌') return 'index'# index = outter(index) # index = get_time# print(index)index() # get_time() -> 执行index()@outterdef login(name): time.sleep(1) print(f'{name} is sb') return 'login'login = outter(login) # login = get_timeprint(login)login('egon') # get_time('egon')——>执行真正的login()@outterdef home(*args,**kwargs): time.sleep(1) return 'home'home = outter(home) # home = get_time()print(home)home() # get_time()——>执行真正的home()
语法糖会将距紧挨着它的可调用对象的名字当做参数自动传入调用outter,自动执行outter函数
@outterdef index() pass
无参装饰器模板
def outter(func): def inner(*args,**kwargs): print('执行被装饰函数之前,你可以做的操作') res = func(*args,**kwargs) print('执行被装饰函数之后,你可以做的操作') return res return inner
import timeuser_auth = {'is_login':None}def login_deco(func): def wrapper(*args,**kwargs): if user_auth['is_login']: # 已经登录了 res = func(*args,**kwargs) return res else: username = input('请输入你的用户名>>>>>>:').strip() # 没有登录,执行登录 pwd = input('请输入你的密码>>>>>:').strip() if username == 'nick'and pwd =='123': print('恭喜登录成功') user_auth['is_login'] = True res = func(*args,**kwargs) return res else: print('账号密码错误') return wrapper@login_decodef index(name): time.sleep(0.5) print(f'{name} is dsb') return 666@login_decodef home(): time.sleep(0.5) print('home') return 9999index('nick')home()
有参装饰器(最复杂就三层)
def sanceng(data): def deco(func): def wrapper(*args,**kwargs): if data == 'file': # 执行被装饰器函数之前你可以做的操作 res = func(*args,**kwargs) # 执行被装饰函数之后你可以做的操作 return res return wrapper return deco
装饰器在装饰的时候,顺序从下往上。装饰器在执行的时候,顺序从上往下。