一、迭代器
可以进行for循环的都是可迭代对象,常见的可迭代对象:str、list、tuple、dict、set、range、f(文件),它们都遵循可迭代协议。
s = "小黑人" for el in s: print(el)
Iterable:可迭代对象,内部包含__iter__()函数。
Iterator:迭代器,内部包含__iter__同时还包含__next__()。
想要查看某数据类型是否为可迭代对象或者迭代器,可以通过dir()函数查看时候包含__iter__或者__iter__和__next()__。
通过__iter__()可以获取到对象的迭代器,使用迭代器中的__next__()来获取迭代器中的元素。
s = "小黑人" c = s.__iter__() #获取迭代器 print(c.__next__()) #使用迭代器进行迭代,获取一个元素,小 print(c.__next__()) #黑 print(c.__next__()) #人 print(c.__next__()) #StopIteration
迭代器特点:
1.节省内存。
2.惰性机制。
3.只能向下执行,不能反复。
补充:for循环机制
s = "小黑人" c = s.__iter__() while True: try: i = c.__next__() print(i) except StopIteration: break
二、生成器
生成器实质就是迭代器。获取生成器有三种方法:生成器函数、推导式获取生成器、通过数据转换获取生成器。
1.生成器函数
def func(): print("小黑人") yield 114 #把函数中return改成yield,这个函数就是生成器函数 print("小黑人不黑") yield 3114 #原来时0114,但是会报错,0不能打头,语法不支持。 ret = func() print(ret) ss = ret.__next__() print(ss) ss = ret.__next__() print(ss) 结果:<generator object func at 0x0000027203D4A2A0> 小黑人 114
小黑人不黑
3114
注:执行这个函数时,就不是函数的执行,而是获取这个生成器。生成器本质时迭代器,可以通过__next__()来执行生成器。
yield和return效果一样,遇到return是直接停止执行函数,而yield是分段来执行一个函数(当内容多时,一次性全部输出,会占大量内存;而用yield时,next一此就输出一个,不占内存)。当程序运行完最后一个yield,后面继续进行__next__()时程序会报错。
生成器也可以用for循环获取内部元素。因为生成器就是迭代器,迭代器为可迭代对象,故可以进行for循环。
补充send:
1.send()和__next__()一样,都可以让生成器执行到下一个yield;
2.send可以给上一个yield的位置传递值,但不能给最后一个yield传值,同时在第一次执行生成器代码时不能使用send()。
def func(): print("小黑人") a = yield 114 #把函数中return改成yield,这个函数就是生成器函数 print("a=",a) yield 3114 #原来时0114,但是会报错,0不能打头,语法不支持。 ret = func() ss = ret.__next__() print(ss) ss = ret.send("小黑人不黑") print(ss) 结果: 小黑人 114 a= 小黑人不黑 3114
2.生成器表达式
列表、字典、集合都有推导式,元组没有推导式。
列表推导式:[结果 fof 变量 in 可迭代对象 if 筛选]
字典推导式:{结果 fof 变量 in 可迭代对象 if 筛选} 结果:key:value
集合推导式:{结果 fof 变量 in 可迭代对象 if 筛选} 结果:key
eg:列表推导式:list = [结果 for in 可迭代对象 if 条件]
lst = ["小黑人%s" % i for i in range3)] print(lst) 结果: ['小黑人0', '小黑人1', '小黑人2']
列表推导式代码简单,但是出现错误之后不易排查。
生成器表达式和列表推导式语法一样,只是把[]换成()。
lst =("小黑人%s" % i for i in range(0)) print(lst) 结果: <generator object <genexpr> at 0x0000017602B9A2A0>
生成器表达式和列表推导式区别:
1.列表推导式比较耗内存,一次性加载,生成器表达式几乎不占内存,使用时才分配和使用内存。
2.得到的值不一样。列表推导式得到的是一个列表,生成器表达式获取的是一个生成器。
注:生成器有惰性机制:生成器只有在方位的时候才有值。说白了,找它要才会给值,不找它要,它是不会执行的。
def func(): print(111) yield 222 g = func() #生成器g g1 = (for i in g) #生成器g1,但是g1的数据来源于g g2 = for i in g1) #生成器g2,但是g2的数据来源于g1 print(list(g)) #获取g中的数据,这时func()才会被执行,打印111,获取222,g完毕 print(list(g1)) #获取g1中的数据,g1的数据来源是g,但g已经取完,没有数据了。 print(list(g2)) #和g1相同
测试题
def add(a,b): return a + b def test(): for r_i in range(4): yield r_i g = test() for n in [2,10]: g = (add(n,i) for i in g) ''' 展开一层:g = (add(n,i) for i in add(n,i) for i in g), 展开第二层:g = (add(n,i) for i in add(n,i) for i in test()), 两个n在取值时不可能不同,取值时为10。 ''' print(g) print(list(g)) #惰性机制,不到最后不会拿值。 结果: <generator object <genexpr> at 0x000001C5C0C6A408> [20, 21, 22, 23]