参考自:https://www.cnblogs.com/wj-1314/p/8490822.html
参考自:https://blog.csdn.net/qq_39521554/article/details/79864889
可迭代对象(iterable)
可迭代对象(iterable):可直接作用于for循环的对象的统称,如list、tuple、dict、set、str、生成器等。
可以用isinstance()判断一个对象是否为iterable:
>>> from collections import Iterable
>>> isinstance([], Iterable)
True
>>> isinstance({}, Iterable)
True
>>> isinstance('abc', Iterable)
True
>>> isinstance((x for x in range(10)), Iterable)
True
>>> isinstance(100, Iterable)
False
迭代器(iterator)
迭代器(iterator):可以作用于next()函数的对象的统称。
python中iterator对象表示一个数据流,不能提前知道数据流的长度,只能通过next()调用。所以说iterator的计算是惰性的,只有在需要返回的时候才会返回下一个数据。
可以用isinstance()判断一个对象是否为iterator:
>>> from collections import Iterator
>>> isinstance((x for x in range(10)), Iterator)
True
>>> isinstance([], Iterator)
False
>>> isinstance({}, Iterator)
False
>>> isinstance('abc', Iterator)
False
用iter()函数可以把list
、dict
、str
等Iterable
变成Iterator:
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
用next()函数,使iterator返回下一个值:
>>> a = iter([1,2,3])
>>> print a.next()
1
>>> print a.next()
2
>>> print a.next()
3
>>> print a.next()
Traceback (most recent call last):
File "G:/study/python/code/book_test/test.py", line 18, in <module>
print a.next()
StopIteration
生成器(generator)
生成器(generator):使用了yield的函数叫做generator
它是特殊的iterator
每次遇到 yield 时,函数会暂停并保存当前所有的运行信息(挂起),返回 yield 的值(类似return), 并在下一次执行 next() 方法时从yield下一行继续运行(重新拾起)。
实现一个生成器方法1,用生成器函数:
# 用生成器实现斐波那契数列
def fib(max):
n,a,b =0,0,1
while n < max:
yield b
a,b =b,a+b
n = n+1
for i in fib(6):
print(i)
结果:
1
1
2
3
5
8
实现一个生成器方法2,把列表生成式的[]换成():
# 列表生成式
lis = [x*x for x in range(10)]
print(lis)
# 生成器
generator_ex = (x*x for x in range(10))
print(generator_ex)
结果:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
<generator object <genexpr> at 0x000002A4CBF9EBA0>
用yield实现“在单线程的情况下实现并发运算的效果”:
import time
def consumer(name):
print("%s 准备学习啦!" %name)
while True:
lesson = yield # 没有send()时候,此处返回None;send()之后,此处返回send()过来的数值
print("开始[%s]了,[%s]老师来讲课了!" %(lesson,name))
def producer():
c = consumer('A')
c2 = consumer('B')
c.next()
c2.next()
print("同学们开始上课 了!")
for i in range(10):
time.sleep(1)
print("到了两个同学!")
c.send(i) # 把一个值发送到yield后面
c2.send(i)
producer()
结果:
A 准备学习啦!
B 准备学习啦!
同学们开始上课 了!
到了两个同学!
开始[0]了,[A]老师来讲课了!
开始[0]了,[B]老师来讲课了!
到了两个同学!
开始[1]了,[A]老师来讲课了!
开始[1]了,[B]老师来讲课了!
到了两个同学!
开始[2]了,[A]老师来讲课了!
开始[2]了,[B]老师来讲课了!
到了两个同学!
开始[3]了,[A]老师来讲课了!
开始[3]了,[B]老师来讲课了!
到了两个同学!
开始[4]了,[A]老师来讲课了!
开始[4]了,[B]老师来讲课了!
到了两个同学!
开始[5]了,[A]老师来讲课了!
开始[5]了,[B]老师来讲课了!
到了两个同学!
开始[6]了,[A]老师来讲课了!
开始[6]了,[B]老师来讲课了!
到了两个同学!
可迭代、迭代器、生成器总结
- 可直接作用于for循环的就是可迭代的
- 可直接作用于next()的就是可迭代对象
- 用yield的函数就是生成器,列表生成式的[]换成()也能得到生成器
杂:
- 同样要完成迭代,使用生成器(for i in range(5))相对使用列表生成式[for i in range(5)]的好处:前者节省内存,后者生成的数据都在内存中,浪费内存。
- c.next() 和 c.send(None) 作用相同,区别在于send可传递参数给yield表达式。(详细区别日后添加)
- 第一次调用时,必须使用next()语句和send(None)之一,不能使用send发送一个非None的值,否则会出错,因为这时没有yield语句来接收这个值。
def consumer():
r = 'here'
while True:
# 1.先执行等号右边,所以直到第一次跳出函数,也没有给n1赋值。
# 2.yield r 相当于一个整体,赋值给它。
n1 = yield r
if not n1:
return
print n1
r = '%d00 OK' % n1
def produce(c):
aa = c.send(None)
n = 0
while n < 5:
n = n + 1
print n
r1 = c.send(n)
print r1
c.close()
c = consumer()
produce(c)
结果:
1
1
100 OK
2
2
200 OK
3
3
300 OK
4
4
400 OK
5
5
500 OK
下面说明下send执行的顺序。
先记住,n1 = yield r 这行是从右往左执行的。当第一次send(None)(对应11行)时,启动生成器,从生成器函数的第一行代码开始执行,直到第一次执行完yield(对应第4行)后,跳出生成器函数。这个过程中,n1一直没有定义。
运行到send(1)时,进入生成器函数,此时,将yield r看做一个整体,赋值给它。