总目录:https://www.jianshu.com/p/e406a9bc93a9
Python - 子目录:https://www.jianshu.com/p/50b432cb9460
参考:https://www.cnblogs.com/wj-1314/p/8490822.html
生成器
生成器和之前的推导式其实是极其相似的,但是区别在于:
推导式是 一次性生成一个列表
>>>[x+1 for x in range(10) ]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
生成器是通过相应的算法推导出来的值
>>> g=(x+1 for x in range(10))
>>>g
at 0x000002A4CBF9EBA0> >>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4
>>> next(g)
5
>>> next(g)
6
>>> next(g)
7
>>> next(g)
8
>>> next(g)
9
>>> next(g)
10
>>> next(g)
Traceback (most recent call last):
File "
", line 1, in StopIteration
其实最简单的区分方法就是推导式是[],生成器是()。
既然推导式和生成器作用和写法都差不多,那么为什么还要使用生成器呢,因为: 在数据量较大的时候,你不可能一次性生成那么大的列表,生成器是最好的选择。不仅节约很大的存储空间,还可以按照某种算法推算下去。
在Python中,这种一边循环一边计算的机制,称为生成器:generator。
生成器是一个特殊的程序,可以被用作控制循环的迭代行为。生成器类似一个返回值为数组的函数,这个函数可以接受参数,可以被调用,但是,这个函数一次只能产生一个值,这样消耗的内存数量将大大减少。
因此生成器看起来像是一个函数,但是表现得却像是迭代器。
当然,我们最好还是用for循环来输出一个生成器。
g = (x+1 for x in range(10))
for i in g:
print(i)
1
2
3
4
5
6
7
8
9
10
这样我们就不需要关心StopIteration的错误。
不过这样的生成器太过简单,只能用于一些简单的算法,一些复杂的算法最好通过函数来展示。
斐波那契数列
def fib(max):
n,a,b =0,0,1
while n < max:
yield b
a,b =b,a+b
n = n+1
return 'done'
a = fib(10)
print(fib(10))
这样就会返回一个生成器,之后遍历生成器就可以得到答案。
对yield的总结
(1)通常的for..in...循环中,in后面是一个数组,这个数组就是一个可迭代对象,类似的还有链表,字符串,文件。他可以是a = [1,2,3],也可以是a = [x*x for x in range(3)]。
它的缺点也很明显,就是所有数据都在内存里面,如果有海量的数据,将会非常耗内存。
(2)生成器是可以迭代的,但是只可以读取它一次。因为用的时候才生成,比如a = (x*x for x in range(3))。!!!!注意这里是小括号而不是方括号。
(3)生成器(generator)能够迭代的关键是他有next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。
(4)带有yield的函数不再是一个普通的函数,而是一个生成器generator,可用于迭代
(5)yield是一个类似return 的关键字,迭代一次遇到yield的时候就返回yield后面或者右面的值。而且下一次迭代的时候,从上一次迭代遇到的yield后面的代码开始执行
(6)yield就是return返回的一个值,并且记住这个返回的位置。下一次迭代就从这个位置开始。
(7)带有yield的函数不仅仅是只用于for循环,而且可用于某个函数的参数,只要这个函数的参数也允许迭代参数。
(8)send()和next()的区别就在于send可传递参数给yield表达式,这时候传递的参数就会作为yield表达式的值,而yield的参数是返回给调用者的值,也就是说send可以强行修改上一个yield表达式值。
(9)send()和next()都有返回值,他们的返回值是当前迭代遇到的yield的时候,yield后面表达式的值,其实就是当前迭代yield后面的参数。
(10)第一次调用时候必须先next()或send(),否则会报错,send后之所以为None是因为这时候没有上一个yield,所以也可以认为next()等同于send(None)
迭代器(迭代就是循环)
生成器一定是迭代器,迭代器不一定是生成器。
迭代器包含有next方法的实现,在正确的范围内返回期待的数据以及超出范围后能够抛出StopIteration的错误停止迭代。
s='hello' #字符串是可迭代对象,但不是迭代器
l=[1,2,3,4] #列表是可迭代对象,但不是迭代器
t=(1,2,3) #元组是可迭代对象,但不是迭代器
d={'a':1} #字典是可迭代对象,但不是迭代器
set={1,2,3} #集合是可迭代对象,但不是迭代器
# *************************************
f=open('test.txt') #文件是可迭代对象,是迭代器
#如何判断是可迭代对象,只有__iter__方法,执行该方法得到的迭代器对象。
# 及可迭代对象通过__iter__转成迭代器对象
from collections import Iterator #迭代器
from collections import Iterable #可迭代对象
print(isinstance(s,Iterator)) #判断是不是迭代器
print(isinstance(s,Iterable)) #判断是不是可迭代对象
#把可迭代对象转换为迭代器
print(isinstance(iter(s),Iterator))
小结
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。