5.12 迭代器(Iterator)
在python中,一边循环一边计算后面元素的机制(惰性运算),称为迭代器:Iterator。
迭代是Python最强大的功能之一,是访问集合元素的一种方式。
迭代器有两个基本的方法:iter() 和 next()。
或者 .iter() 和 next() 方法。
for的表达式用()括起来可以创建一个迭代器
- 迭代器的特点:
- 迭代器一定是一个可循环的(for,while等)
- 迭代器是一个可以记住遍历的位置的对象(迭代器内部有一个计数器,会记录执行到第几次循环)
- 迭代器只能往前不会后退。
- 迭代器进行下一步用next()函数。(也可以用 .next() 方法)
- 当执行到循环最后一次,继续next()则会报错StopIteration
- next(g)执行到一半,然后用'for i in g'来循环的话,会从记录到的位置开始继续循环
python3中,用for循环循环可迭代数据类型的时候,其实内部先使用iter把数据转成迭代器,再用next执行迭代器。
迭代器例1:
g = (x*x for x in range(3)) # 生成一个迭代器。迭代器赋值只赋值了算法,并没有值生成。这个写法其实是一个生成器,语法糖里有不可见的yield
print(g) # 打印一个迭代器对象的内存地址,没有值
print(next(g)) # 执行迭代器用next(),一次next只返回循环内的一次运算结果。
print(next(g)) # 返回第二次循环内运算的结果。
print(next(g)) # 返回第三次循环内运算的结果。
# print(next(g)) # next超过循环的最大次数,报错StopIteration
执行结果:
<generator object <genexpr> at 0x00000000020B3570>
0
1
4
generator(生成器)是迭代器的一种
迭代器例2:
g = (x*x for x in range(6))
print(next(g)) # 执行迭代器用next(),一次next只返回循环内的一次运算结果。
print(next(g)) # 返回第二次循环内运算的结果。
for i in g: # 这样可以遍历迭代器器剩下的所有元素。因为迭代器内部有计数器,所以会接着上次next()执行到的位置开始循环
print(i) # 这里打印4
# print(next(g)) # 因为for遍历了所有迭代器元素,计数器就到了最后。所以这里会报错
执行结果:
0
1
4
9
16
25
python3的range()用的就是迭代器的算法。py2则是把for循环的元素放入一个列表,很低效。
5.13 生成器 (generator) yield
生成器:python中,使用了yield的函数被称为生成器(generator)
yield:类似于return,返回b。但是与return不同,不会中断程序。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
生成器的yield必须在循环内(迭代器必须是可迭代的)
功能在下面的代码例里面注释说明:
代码例:
# 斐波那契
def fib(n):
a = 0
b = 1
for i in range(1,n+1):
temp = a + b
a = b
b = temp
yield b # 类似于return,返回b。但是与return不同,不会中断程序。
print(fib(10)) # 会返回一个generator的内存地址。
f = fib(10)
print(next(f)) # 与迭代器一样,执行一步。返回第一次循环的b
print(f.__next__()) # 执行第二步。 next(f) 和 f.__next__() 的效果是完全一样的。
print('====do something else...') # 可以在函数执行中,中断函数插入别的内容
print(next(f)) # 执行第三步
print(next(f)) # 执行第四步
for i in f: # 用这个可以把剩下的都执行完。原理和迭代器一样
print(i)
扩展-复杂写法:
# 请使用生成器表达式将列表li = [1, 2, 3, 4, 5, 6, 7, 8, 9]中大于5和小于5的元素乘以2
li = [1, 2, 3, 4, 5, 6, 7, 8, 9]
g = (i * 2 for i in li if i > 5 or i < 5)
5.14 用生成器来实现并发编程
- send(self,value)
向生成器的yield传入值。send相当于:先传入参数 -> 再next()
例:
def g_receive():
while True:
n = yield # 这个作用是从外面接受yield的返回值。
print('receive var is :',n)
# 注意,生成器内包含yield循环不需要设置结束,func调用后会自动停止在最后一次调用的地方,但也不会跳出循环。
# 如果循环次数有上限,注意send()的次数不能超过循环上限。
# send()传入的参数也不可以作为while的结束条件,这样会发生StopIteration 错误
g = g_receive() # 当调用生成器函数的时候,函数只是返回了一个生成器对象,并没有 执行。
g.__next__() # 调用生成器:第一个传入生成器的值必须是None,用.__next__() 或 next(g) 或 .send(None)会发送None到yield。
# 当next方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止。第一次next相当于启动。
# 第一次next执行完后,我们才可以用send来传值。
for i in range(10):
g.send(i) # 调用生成器:传i给yield。
作用:实现单线程下的多并发效果。(线程就是cpu执行的任务单元)
- 简单的单线程多并发例:
def output():
count = 1
while count <= 100:
n = yield
print('收到数据%s' % (n,))
g1 = output()
g2 = output()
g3 = output()
g1.__next__()
g2.__next__()
g3.__next__()
for i in range(100):
g1.send(i+1)
g2.send(i*2)
g3.send(i/2)
5.15 可迭代类型和迭代器
- 可以直接作用于for循环的数据类型(可迭代的数据类型)有以下几种:
- 一类是集合数据类型,如list,tuple,dict,set,str 等
- 一类是generator,包括生成器和带yield的generator function
这些可以直接用作于for循环的对象统称为可迭代类型:iterable,可迭代的意思就是可遍历,可循环。
可以用isinstance(o, Iterable)来判断对象是否可迭代。
Iterable在collections模块中
from collections import Iterable,Iterator # 判断迭代器类型需要导入模块
isinstance({},Iterable) # 判断是可迭代的。返回True
isinstance({},Iterator) # 判断是迭代器。返回False。因为字典,列表,字符串没有next()方法。不是迭代器
isinstance((x for x in range(10)),Iterator) # 是迭代器。返回True
iter({}) # 把可迭代对象转成迭代器,转完了就可以用next了
生成器是迭代器的一种。