闭包的引入
闭包:首先必须是嵌套函数,再次内部函数调用外部函数的变量,如下代码
def outer(): a = 1 def inner(): print("内部函数>",a) print(inner.__closure__) # 用来判断闭包的,(<cell at 0x004573D0: int object at 0x53416880>,) outer()
外部函数的返回值是内部函数的函数名
def outer(): a = 1 def inner(): print("内部函数>",a) return inner res = outer() res()
闭包的小小应用:
from urllib.request import urlopen def get_url(): url = 'http://www.xiaohua100.cn/index.html' def inner(): url_res = urlopen(url).read() print(url_res) return inner get_url()
总结:闭包好处,多次调用函数,不必多次生成外部函数的变量。
装饰器
装饰器的引出,计算程序的执行时间
import time def func(): time.sleep(2) def timer(f): start = time.time() f() end = time.time() print('函数运行的时间是:',end-start) timer(func)
虽然上面的代码可以将实现将计算时间的函数分离,但是改变了原函数额调用方式
def func(): time.sleep(2) def timer(f): # 装饰器函数 def inner(): start = time.time() f() # 被装饰函数 end = time.time() print('函数运行的时间是:',end-start) return inner func = timer(func) func()
装饰器的作用:不想修改原来函数的调用方式,但是还想再原来的函数前后添加功能。
最终版没有改变函数的调用方式
def timer(f): # 装饰器函数 def inner(): start = time.time() f() # 被装饰函数 end = time.time() print('函数运行的时间是:',end-start) return inner @timer # 语法糖,@装饰器函数名 def func(): time.sleep(2) func()
带返回值的装饰器
def timer(f): # 装饰器函数 def inner(): start = time.time() res = f() # 被装饰函数 end = time.time() print('函数运行的时间是:',end-start) return res return inner @timer # 语法糖,@装饰器函数名 def func(): time.sleep(2) return 'ok' res = func() print(res)
装饰带参数的函数装饰器
def timer(f): # 装饰器函数 def inner(*args,**kwargs): start = time.time() res = f(*args,**kwargs) # 被装饰函数 end = time.time() print('函数运行的时间是:',end-start) return res return inner @timer # 语法糖,@装饰器函数名 def func(a): time.sleep(2) print('参数:',a) return 'ok' res = func(1) print(res)
装饰器的最终形式
def wrapper(func): def inner(*args,**kwargs): ret = func(*args,**kwargs) return ret return inner @wrapper def qqxing(): # qqing=wrapper(qqxing) print(123) qqxing() print(qqxing.__name__) # inner
例子:通过使用装饰器来将要执行的函数的函数名写入文件中
import datetime def log(func): def inner(*args,**kwargs): with open('log.txt','a',encoding='utf8') as f: date_str = str(datetime.datetime.now()) f.write(date_str+func.__name__+' ') ret = func(*args,**kwargs) return ret return inner @log def qqxing(): print('qqxing') qqxing()
通过别人写的装饰器模块,来得到被装饰函数的函数名
import datetime from functools import wraps def log(func): @wraps(func) def inner(*args,**kwargs): with open('log.txt','a',encoding='utf8') as f: date_str = str(datetime.datetime.now()) f.write(date_str+func.__name__+' ') ret = func(*args,**kwargs) return ret return inner @log def qqxing(): print('qqxing') qqxing() print(qqxing.__name__) # 如果没有加@wraps(func),qqxing__name__是inner,@wraps也成为装饰器修复技术
装饰器进阶
1、三层装饰器
import time Flag = True def timer_out(flag): def timer(func): def inner(*args,**kwargs): if flag: start = time.time() ret = func(*args,**kwargs) end = time.time() print(end-start) return ret else: ret = func(*args, **kwargs) return ret return inner return timer # timer = timer_out(Flag) @timer_out(Flag) # <==>@timer def wahaha(): time.sleep(2) print('wahahah') wahaha()
2、@wrapper2
@wrapper1
如上所示的装饰器
def warpper1(func): def inner1(*args,**kwargs): print("wrapper1,before func") func() print("wrapper1,after func") return inner1 def warpper2(func): def inner2(*args,**kwargs): print("wrapper2,before func") func() print("wrapper2,after func") return inner2 @warpper2 # foo=wrapper(foo)-->wrapper(inner1)==inner2 @warpper1 # 先执行这个装饰器 foo=warpper1(foo)==inner1 def foo(): print('foo') foo() #结果 # wrapper2,before func # wrapper1,before func # foo # wrapper1,after func # wrapper2,after func
3、面试题:免登录版装饰器
Flag = False def login(func): def inner(*args,**kwargs): global Flag if Flag: ret = func() return ret else: username = input('username:') password = input('password:') if username=='jack' and password=='123': Flag=True ret = func(*args,**kwargs) return ret else: print('登录失败') return inner
4、装饰器版的登录校验
这个例子其中的一些方法会在Django框架中学习到,这里只是为了总结装饰器而已。
def check_login(func): def inner(requset,*args,**kwargs): ret = requset.get_signed_cookie("is_login",default="0",salt="salt") # 加盐的cookie if ret == '1': # 已经登录过的继续执行 return func(requset,*args,**kwargs) else: next_url = requset.path_info # 获取当前访问的URL return redirect("login/?next={}".format(next_url)) return inner
生成器
如下两种形式都是生成器
1、生成器函数:只要含有yield关键字的函数都是生成器,类似return,只能写在函数内部,不能和return共用。而且yield语句一次返回一个结果,在每个结果中间挂起原来的状态,以便下次从它开始离开的地方继续执行。
首先看普通函数的返回值
def generator(): print('============') return '返回值函数' g = generator() print(g)
再看含有yield 的生成器函数
def generator(): print('>>>>>>>>>>') yield '生成器函数' g = generator() print(g.__next__()) print(next(g)) # 生成器函数 # Traceback (most recent call last): # File "D:/pythonstudy/装饰器迭代器生成器/生成器.py", line 16, in <module> # print(next(g)) # StopIteration 如果取不到了值会报错为StopIteration
def generator(): print(1) yield 'a' print(2) yield 'b' g = generator() print(g.__next__()) print(next(g))
生成器进阶
def generator(): print(123) content = yield 1 print('content:', content) print(456) yield 2 g = generator() print(g.__next__()) print(g.send('hello')) # send()和next()的效果一样
send获取下一个值的效果和next基本一样,只是在获取下一个值的时候,给上一个值的位置传递一个数据。
注意:第一次使用生成器的时候必须用next获取第一个值。
最后一个yield不能接受外部的值。
生成器面试题
def demo(): for i in range(4): yield i g = demo() g1 = (i for i in g) g2 = (i for i in g1) print(list(g1)) #[0, 1, 2, 3] print(list(g2)) #[]
注释:g1从g中取值直到取完,g2从g1中取到g1末,但是g2开始取值的时候,g1已经为空,所以该结果为空。
2、生成器表达式:本质迭代器【自带__iter__方法和__next__方法】
列表解析式 egg_list = ['鸡蛋%s'%i for i in range(10)]
生成器表达式 laomuji=('鸡蛋%s'%i for i in range(10))
迭代器
1、什么叫迭代
字符串、列表、元祖、字典、集合都可以被for循环,也可以通过Iterable来进行证明,这些都是可迭代的。
from collections import Iterable l = [11,22,33] t = (11,22,33) d = {"name":"jack"} s = {11,22,33} print(isinstance(l,Iterable)) # True print(isinstance(t,Iterable)) #True print(isinstance(d,Iterable)) #True print(isinstance(s,Iterable)) #True
2、可迭代协议
可迭代协议定义非常简单,就是内部实现了__iter__方法。
迭代器遵循迭代器协议:必须使用__iter__方法和__next__方法
3、类中的__iter__和__next__方法
class Foo: def __init__(self,n): self.n = n def __iter__(self): # 成为一个可迭代对象。 return self def __next__(self): self.n += 1 return self.n f = Foo(10) f.__next__() f.__next__() for i in f: if i>20: break print(i)
小练习:迭代器实现斐波那契数列
class Fib: def __init__(self,a,b): self._a = a self._b = b def __iter__(self): return self def __next__(self): self._a,self._b = self._b,self._a + self._b return self._a f = Fib(1,1) print(f.__next__()) # 1 print(f.__next__()) # 2 print(f.__next__()) # 3 print(f.__next__()) # 5 print(f.__next__()) # 8 print(f.__next__()) # 13
迭代器的好处:
1、从容器中一个一个的取值,会把所有的值都取到
2、节省内存空间,迭代器并不会在内存中再占用一大块内存,而是随着循环每次生成一个,每次next每次回给出一个值。