迭代器
迭代器协议
迭代定义:
迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。
1迭代器协议:对象必须提供一个__next__() 方法,执行该方法要么返回迭代中的下一项,要么就引起一个StopIteration异常,以终止迭代(只能往前,不能退后)
2可迭代对象(迭代器):实现了迭代器协议的对象(如何实现迭代器协议,在对象内部定义__iter__()方法
3协议是一种约定,可迭代对象实现了迭代器协议,Python的内部函数工具(如for循环,sum,min,max函数等)都是使用迭代器协议访问对象!!!
迭代器作用:可以节省内存
迭代器相关的方法:iter() 和 next()。
python内置函数 next() 本质就是在调用__next__()
>>> list=[1,2,3,4] >>> it = iter(list) # 创建迭代器对象 >>> print (next(it)) # 输出迭代器的下一个元素 1 >>> print (next(it)) 2 >>>
通过iter()还可以指定迭代至结束时的对象
l = ['a', 'b', 'c', 'd'] def test(): return l.pop() x = iter(test, 'b') print(x.__next__()) #d print(x.__next__()) #c print(x.__next__()) #到'b'了,抛异常
python中的for机制
(字符串str,列表list,元组tunple,字典dict,,集合set,文件对象)这些都不是可迭代对象,只不过在for循环,调用了他们的内部的__iter__()方法,把他们变成了可迭代对象,(补充:为什么str,list,tunple可以有下标,例如list[0],因为他们是有序的)
for循环把他们变成了可迭代对象后,就调用可迭代对象的_next()_方法去取值,而且for循环会捕获StopIteration异常,以终止迭代!!!
1 name = 'ales' 2 print(n) 3 4 输出结果 5 <str_iterator object at 0x00E7EF90>
for循环就是基于迭代器协议提供一个统一的可以遍历所有对象的方法,即在遍历之前,先调用对象的__iter__()方法将其转换成一个迭代器,然后使用迭代器协议去实现循环访问。这样所有的对象都可以通过for循环来遍历。name = 'ales'n = name.__iter__() #遵循迭代协议,调用_iter_方法生成可迭代对象
print(n.__next__()) print(n.__next__()) print(n.__next__()) print(n.__next__()) print(n.__next__())# 迭代至StopIteration异常 输出结果 a l e s Traceback (most recent call last): File "E:/PycharmProjects/untitled/day18/迭代器.py", line 9, in <module> print(n.__next__()) StopIteration
利用while模仿for循环
1 list1 = [1, 2, 3, 4] 2 l = list1.__iter__() 3 while True: 4 try: 5 print(l.__next__()) 6 except StopIteration: 7 break
执行结果
1
2
3
4
生成器
什么是生成器
可以理解为一种数据类型,这种数据类型自动实现了迭代器协议,其他的数据类型需要需要调用自己内置的__iter__方法,所以生成器就是可迭代对象
python中有两种方式提供生成器:
1.生成器函数:常规函数定义,但是使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中,挂起函数的状态,以便下次从它离开的地方继续执行
2.生成器表达式:类似于列表解析,但是生成器返回按需产生结果的一个对象,而不是一次构建出的结果列表
生成器函数
def test(): yield 1 yield 2 yield 3 x = test() print(x) print(x.__next__()) print(x.__next__()) print(x.__next__()) print(x.__next__()) 执行结果 <generator object test at 0x00C834F0> 1 2 3 print(x.__next__()) StopIteration
生成器函数和普通函数
普通函数
# 普通函数 def test(): print('start') test() 运行结果 start
生成器函数
# 生成器函数是一个可迭代对象,注意与普通函数的区别 def test(): print('start') yield '这是第一个yield语句的返回值' print('这是第一个yield的下一行') yield '这是第二个yield语句的返回值' print('这是第二个yield的下一行') yield '这是第三个yield语句的返回值' print('这是第三个yield的下一行') a = test() #生成一个可迭代对象 print(a) print(type(a)) 运行结果 <generator object test at 0x02DF35B0> <class 'generator'>
接上
第一种
a.__next__() #运行至第一个yield语句结束,并保留运行状态 a.__next__() #从上次的运行状态开始,运行至第二个yield语句结束 a.__next__() 运行结果 start 这是第一个yield的下一行 这是第二个yield的下一行
第二种
res = a.__next__() ##运行至第一个yield语句结束,保留运行状态,并将yield的返回值赋值给res print(res) 运行结果 start 1
第三种
res = a.__next__() #运行至第一个yield语句结束,保留运行状态,并将yield的返回值赋值给res print(res) a.__next__() #从上次的运行状态开始,运行至第二个yield语句结束 运行结果 start 这是第一个yield语句的返回值 这是第一个yield的下一行
生成器表达式
num = ('num%d' %i for i in range(10))#生成器表达式 print(num) print((num.__next__())) print((num.__next__())) print(next(num)) print(next(num)) 运行结果 <generator object <genexpr> at 0x00AA34F0> num0 num1 num2 num3
列表解析和生成器表达式
>>> l = [i for i in range(0,15)] #列表解析 >>> print(l) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] >>> m = (i for i in range(0,15)) #生成器表达式 >>> print(m) <generator object <genexpr> at 0x104b6f258> >>> for g in m: ... print(g,end=', ') ... 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
把列表解析的 [ ] 换成 ( ) 得到的就是生成器表达式
列表解析和生成器表达式都是遍历的编程方式,但是生成器表达式更节省内存
# 列表解析 print([i for i in range(1,1000000)]) #内存占用过大,机器易卡死 # 生成器表达式 m = (i for i in range(1,100000000)) #几乎不占内存,运行的时间取决于cpu print(sum(m))
python大部分内置函数都是使用迭代器协议访问对象。如 sum()
sum(x**2 for x in range(4)) sum([x ** 2 for x in range(4)]) #不用多此一举构造列表
__next__()
__next__()在生成器函数中可以运行至函数的下一个yield语句结束,并保留运行状态,返回yield语句定义的返回值
next()
next() 返回迭代器的下一个项目。
内置函数 next() 本质就是在调用__next__()
生成器的send用法
def test(): print('start') first = yield 1 print('这是第一个yield的下一行', first) second = yield 2 print('这是第二个yield的下一行', second) yield t = test() #产生生成器 print(t.__next__()) #第一次运行只能使用next或者send(None) t.send('this is the first send value') #send具有next的功能,让程序运行到下一个yield语句结束 t.send('this is the second send value')
运行结果
start 1 这是第一个yield的下一行 this is the first send value 这是第二个yield的下一行 this is the second send value Process finished with exit code 0
使用生成器实现并发
import time def eat_grass(name): print('我是%s,我准备开始吃牧草了' % name) while True: grass = yield time.sleep(1) print('我开心地吃了第%s棵牧草' %grass) # name = 'cow' # eg = eat_grass(name) # eg.__next__() # eg.send('grass_number_01') # eg.send('grass_number_02') # eg.send('grass_number_03') def product_grass(): name = 'cow' eg = eat_grass(name) eg.__next__() for i in range(1,10): time.sleep(1) eg.send('%d' %i) product_grass()
总结
以生成器函数为例进行总结:
1、语法上和函数类似:差别在于常规函数只能使用一个return语句返回值,而生成器可以使用多个yield语句返回值
2、自动实现迭代器协议:Python对于生成器自动实现了迭代器协议,所以可以调用 next,并在没有值可以next的时候,生成器自动产生StopIteration异常。还可以应用到迭代背景中(如for循环,sum函数)
3、状态挂起:生成器使用yield语句返回一个值,此时yield语句挂起该生成器函数的状态,保留足够信息,以便之后的使用都是基于最新的状态
优点:
1、生成器的好处时延迟计算,一次返回一个结果。这样可以节省内存,提高运行效率。对大数据处理非常有用。
见列表解析和生成器表达式的对比
2、生成器可以提供代码的可读性
注意:生成器只能遍历一次(例如生命只有一次,且年龄只会越来越大,不能返老还童)
杂货铺
列表解析
l = [i for i in range(10)]#列表解析 print(l) # 等价于 l = [] for i in range(10): l.append(i) print(l)
三元表达式
age = 20 res = 'adult' if age>20 else 'child' #三元表达式 print(res) # 等价于 age = 20 res = '' if age > 20: res = 'adult' else: res = 'child' print(res)
装饰器
概念和原则
装饰即修饰,意指为其他函数添加新功能;
装饰器的本质就是函数
作用是为其他函数添加新功能,如计算该函数运行时长
装饰器遵循原则:
1.不修改被装饰函数的源代码(开放封闭原则)
2.为被装饰函数添加新功能后,不能修改被修饰函数的调用方式
装饰器的实现 = 高阶函数 + 函数嵌套 + 闭包
高阶函数
高阶函数 = 函数接收的参数是一个函数名 或 函数返回值包含函数名
参数是一个函数名,可以为被修饰函数添加新功能
返回值包含函数,可以不改变被修饰函数的调用方式
1 # 高阶函数:参数包含函数名;或者返回值包含函数名 2 3 import time 4 5 def foo(): 6 time.sleep(1) 7 print('this is from foo') 8 9 def deco(func): #参数是一个函数名,可以为被修饰函数添加新功能 10 starttime = time.time() 11 func() 12 endtime = time.time() 13 print('运行%s函数共耗时%f' %(func,endtime-starttime)) 14 return func #返回值包含函数,可以不改变被修饰函数的调用方式 15 16 foo = deco(foo) 17 foo() 18 # 缺陷:这里被修饰函数运行了两次,所以生成器不能只靠高阶函数!
函数嵌套和闭包
函数嵌套:在函数体中定义函数
闭包:闭包就是能够读取其他函数内部变量的函数。在一个作用域里放入定义变量,相当于打了一个包
def country(address): # name = 'alex' print('this is from country') def province(): # name = 'blice' print('this is from province %s' %address) country('我的地址')
装饰器的使用
1 import time 2 3 def deco(func): 4 def qiantao(): 5 starttime = time.time() 6 func() 7 endtime = time.time() 8 print('运行%s函数共耗时%f' %(func,endtime-starttime)) 9 return qiantao 10 11 12 def foo(): 13 time.sleep(1) 14 print('this is from foo') 15 16 17 foo = deco(foo) #deco(foo)就是qiantao 18 foo() #这里foo()相当于qiantao()
1 import time 2 3 def deco(func): 4 def qiantao(): 5 starttime = time.time() 6 res = func() 7 endtime = time.time() 8 print('运行%s函数共耗时%f' %(func,endtime-starttime)) 9 return res 10 return qiantao 11 12 13 @deco #就相当于在调用foo()之前执行 foo = deco(foo) 14 def foo(): 15 time.sleep(1) 16 print('this is from foo') 17 return 'foo的返回值' 18 19 20 # foo = deco(foo) 21 res = foo() 22 print(res)
1 import time 2 3 def deco(func): 4 def qiantao(*args, **kwargs): # args=(name, age) kwargs={gender:male,} 5 starttime = time.time() 6 res = func(*args, **kwargs) #*(name,age) **{gender:male} 7 endtime = time.time() 8 print('运行%s函数共耗时%f' %(func,endtime-starttime)) 9 return res 10 return qiantao 11 12 female = '女' 13 @deco #就相当于在调用foo()之前执行 foo = deco(foo) 14 def foo(name, age, gender=female): 15 time.sleep(1) 16 print('this is from foo, =====> my name is %s ,age is %s, gender is %s' %(name, age,gender)) 17 return 'foo的返回值' 18 19 res = foo('liming', 18, gender = '男') 20 print(res)
应用实例:
1 user_list = [{'name':'alex', 'pwd':'123'}, 2 {'name':'belief', 'pwd':'123'}, 3 {'name':'a', 'pwd':'123'},] 4 5 current_dic = {'username':'','login':False} 6 7 def auth_fun(fun): 8 def warp(*args, **kwargs): 9 if current_dic['username'] and current_dic['login']: 10 res = fun(*args, **kwargs) 11 return res 12 username = input('please input your name>') 13 password = input('please input your password>') 14 for user_dic in user_list: 15 if username==user_dic['name'] and password==user_dic['pwd']: 16 current_dic['username'] = username 17 current_dic['login'] = True 18 res = fun(*args, **kwargs) 19 return res 20 else: 21 print('用户名或密码错误') 22 return warp 23 24 # index = auth_fun(index) 25 @auth_fun 26 def index(): 27 print('欢迎访问该网站') 28 29 @auth_fun 30 def home(name): 31 print('这是的%s家页面' %name) 32 33 @auth_fun 34 def shopping_car(name): 35 print('%s的购物车里有%s' %(name, '衣服')) 36 37 index() 38 # home('belief') 39 # shopping_car('belief')
1 user_list = [{'name':'alex', 'pwd':'123'}, 2 {'name':'belief', 'pwd':'123'}, 3 {'name':'a', 'pwd':'123'},] 4 5 current_dic = {'username':'','login':False} 6 7 def auth(type='qq'): 8 '''根据用户的登录方式来进行登录操作''' 9 def auth_fun(fun): 10 def warp(*args, **kwargs): 11 print('登录方式是%s' %type) 12 if type=='qq': 13 if current_dic['username'] and current_dic['login']: 14 res = fun(*args, **kwargs) 15 return res 16 username = input('please input your name>') 17 password = input('please input your password>') 18 for user_dic in user_list: 19 if username==user_dic['name'] and password==user_dic['pwd']: 20 current_dic['username'] = username 21 current_dic['login'] = True 22 res = fun(*args, **kwargs) 23 return res 24 else: 25 print('用户名或密码错误') 26 elif type=='wx': 27 print('打开微信扫一扫') 28 else: 29 print('不支持其他登录方式,请使用qq或微信') 30 return warp 31 return auth_fun 32 33 # index = auth_fun(index) 34 @auth(type='qq') #autn(type='qq')就是auth_fun @auth_fun相当于index=auth_fun(index), 即index = warp 35 def index(): 36 print('欢迎访问该网站') 37 38 @auth(type='wx') 39 def home(name): 40 print('这是的%s家页面' %name) 41 42 @auth(type='sj') 43 def shopping_car(name): 44 print('%s的购物车里有%s' %(name, '衣服')) 45 46 # index() 47 home('belief') 48 # shopping_car('belief')
参考
https://www.runoob.com/python3/python3-iterator-generator.html