python闭包与装饰器
什么是闭包
1 #定义一个函数 2 def test(number): 3 4 #在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包 5 def test_in(number_in): 6 print("in test_in 函数, number_in is %d"%number_in) 7 return number+number_in 8 #其实这里返回的就是闭包的结果 9 return test_in 10 11 12 #给test函数赋值,这个20就是给参数number 13 ret = test(20) 14 15 #注意这里的100其实给参数number_in 16 print(ret(100)) 17 18 #注意这里的200其实给参数number_in 19 print(ret(200))
运行结果
in test_in 函数, number_in is 100 120 in test_in 函数, number_in is 200 220
内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。
1 def func(): 2 name = 'gg' 3 def inner(): 4 print(name) 5 return inner 6 7 f = func() 8 f()
nonlocal访问外部函数的局部变量(python3) ****上一层局部变量,就近原则****
1 def counter(start=0): 2 def incr(): 3 nonlocal start 4 start += 1 5 return start 6 return incr 7 8 c1 = counter(5) 9 print(c1()) 10 print(c1()) 11 12 c2 = counter(50) 13 print(c2()) 14 print(c2()) 15 16 print(c1()) 17 print(c1()) 18 19 print(c2()) 20 print(c2())
判断闭包函数的方法__closure__
1 #输出的__closure__有cell元素 :是闭包函数 2 def func(): 3 name = 'eva' 4 def inner(): 5 print(name) 6 print(inner.__closure__) 7 return inner 8 9 f = func() 10 f() 11 12 #输出的__closure__为None :不是闭包函数 13 name = 'egon' 14 def func2(): 15 def inner(): 16 print(name) 17 print(inner.__closure__) 18 return inner 19 20 f2 = func2() 21 f2()
思考:
1.闭包似优化了变量,原来需要类对象完成的工作,闭包也可以完成 2.由于闭包引用了外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存
装饰器的前世今生
小掌门上班也有一段日子,终于到了发年终奖的时候,但年终奖评比的标准是一个问题,于是有人提议就用每个函数的执行时间来评比年终奖吧
作为团队的小家伙,审核的工作便落在了小掌门身上
通常开发部门的代码是这样的
def func(): print("老板好")
小掌门灵机一动
import time def func(): start=time.time() print("老板好") time.sleep(0.1)#假设为执行时间,实际代码肯定不是一个print能搞定的 print(time.ctime()-start) func()
不知不觉,一个礼拜过去了,小掌门前前后后改了四五千个函数
这可没法忍,老夫做的可是技术,怎么搞得跟苦力一样,得像个办法让自己轻松点,小掌门写了个timer函数,于是又一个代码版本应运而生
小掌门跟所有开发同事讲,你们的审核已经搞定了,你们每个在自己代码中加一句调用时间的函数吧
1 import time 2 def func(): 3 print("老板好") 4 time.sleep(0.1)#假设为执行时间,实际代码肯定不是一个print能搞定的 5 6 start=time.time() 7 func() 8 print(time.ctime()-start)
就这样过了几天,所有的同事见了小掌门都一脸怨气,老大看小掌门的眼神也是充满邪恶。小掌门感到了恐慌,又把之前写的代码拿出来完善了下
于是有了下个版本
1 import time 2 def func(): 3 print("老板好") 4 time.sleep(0.1)#假设为执行时间,实际代码肯定不是一个print能搞定的 5 6 def timer(func): 7 start=time.time() 8 func() 9 print(time.ctime()-start)
小掌门告诉的大家,实际用的时候,自己调用timer就行了,看起来有些不错,但还有些瑕疵 ,有人提示小掌门说可以使用语法糖,接着又一版本出现
1 import time 2 def timer(func): 3 def inner(): 4 start = time.time() 5 func() 6 print(time.time() - start) 7 return inner 8 @timer 9 def func(): 10 print("老板好") 11 time.sleep(0.1)#假设为执行时间,实际代码肯定不是一个print能搞定的 12 13 func()
在需要计算时间的函数前加一句@timer就行了
小掌门想,同事的参数可能带参数,也有可能带返回值啊,上面的代码似乎搞定不了,后来,小掌门潜心研究语法糖,于是就有了最终的版本
1 import time 2 def timer(func): 3 def inner(*args,**kwargs): 4 start = time.time() 5 ret=func() 6 print(time.time() - start) 7 return ret 8 return inner 9 @timer 10 def func(): 11 print("老板好") 12 time.sleep(0.1)#假设为执行时间,实际代码肯定不是一个print能搞定的 13 14 func()
练习:
1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
1 def login(func): 2 login_flag=False 3 def inner(*args,**kwargs): 4 nonlocal login_flag 5 if login_flag==False: 6 username = input("请输入账号") 7 with open('user.txt', 'r') as file: 8 for line in file:#判断用户名是否在文件中 9 info=line.split() 10 if username in info: 11 passwd=input("请输入密码") 12 if passwd==info[1]:#判断密码是否正确 13 login_flag=True 14 ret=func(*args,**kwargs) 15 return ret 16 else: 17 ret = func(*args, **kwargs) 18 return ret 19 return inner
2.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名称写入文件
1 login_flag=False 2 def login(func): 3 def inner(*args,**kwargs): 4 global login_flag 5 if login_flag==False: 6 username = input("请输入账号") 7 with open('user.txt', 'r') as file: 8 for line in file:#判断用户名是否在文件中 9 info=line.split() 10 if username in info: 11 passwd=input("请输入密码") 12 if passwd==info[1]:#判断密码是否正确 13 login_flag=True 14 ret=func(*args,**kwargs) 15 return ret 16 else: 17 ret = func(*args, **kwargs) 18 return ret 19 return inner
进阶作业(选做):
1.编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
1 from urllib.request import urlopen 2 def urlget(url): 3 return urlopen(url).read() 4 url=input("url") 5 print(urlget(url))
2.为题目1编写装饰器,实现缓存网页内容的功能: 具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
1 import os 2 from urllib.request import urlopen 3 4 def webflush(func): 5 def inner(*args,**kwargs): 6 with open('a.txt', 'rb+') as file: 7 if os.path.getsize('a.txt'): 8 return file.read() 9 else: 10 re=func(*args,**kwargs) 11 file.write(re) 12 print(re) 13 return re 14 return inner 15 16 @webflush 17 def urlget(url): 18 return urlopen(url).read() 19 20 21 urlget("https://www.baidu.com")
默写任务:
默写任务: 1.需默写代码&执行过程&使用方法 def timer(func): def inner(*args,**kwargs): '''执行函数之前要做的''' re = func(*args,**kwargs) '''执行函数之后要做的''' return re return inner @timer def aaa():print(‘a’) 2.默写代码 import time def timer(func): def inner(*args,**kwargs): start = time.time() ret = func(*args,**kwargs) print(time.time() - start) return ret return inner @timer #==> func = timer(func) def func(a): print('你好我好大家好') return '新年好'
我的小鱼你醒了
还认识早晨吗
昨夜你曾经说
愿夜幕永不开启