函数进阶之生成器和迭代器
Posted lshedward
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数进阶之生成器和迭代器相关的知识,希望对你有一定的参考价值。
前提:
列表生成式
给列表a里的大于5的每一个值加10
a = [1, 2, 5, 6, 7, 8] a = [i + 10 if i > 5 else i for i in a] # 可以循环任何可循环的东西,不过只能写到列表或元组里。 print(a) # [1, 2, 5, 16, 17, 18]
复杂东西列表生成式写不出来,最多到三元运算了。
正文:
生成器是为了省内存,不一次释放,需要一个取一个。
g = (i for i in range(5)) print(g) # <generator object <genexpr> at 0x101fba048> print(next(g)) # 0 print(next(g)) # 1 print(next(g)) # 2 print(next(g)) # 3 print(next(g)) # 4 print(next(g)) # 到最后没有了,会报错
生成器只能往前走,不能后退。
我们创建了一个generator后,基本上永远不会调用next(),而是通过for循环来迭代它,并且不需要关心StopIteration的错误,for循环会把它自动消化。
g2 = (i for i in range(5)) for i in g2: print(i)
generator非常强大。如果推算的算法比较复杂,用类似列表生成式的for循环无法实现的时候,还可以用函数来实现。
生成器和range
python2里range是直接创建列表。
python3里的range是用生成器做的,是循环到了才创建。
python2里有个xrange,其实就是等于python3里的range。python3只有range,等于是把python2里的那个range去掉了。
用生成器实现斐波那契
def fib(max): n, a, b = 10, 0, 1 while n < max: print(‘before yield‘)yield n # 把函数的执行过程冻结在这一步,并且把b的值,返回给外面的next() print(b) a, b = b, a + b n += 1 return ‘done‘ print(fib(15)) # <generator object fib at 0x101fba048> 里面有yield,函数名加括号,内部代码根本不执行,只是生成一个生成器对象。 f = fib(15) # return function into a generator next(f) # before yield # first time call next() n = next(f) # 1 # before yield next(f) # 1 # before yield next(f) # 2 # before yield print(n) # 11
yield的作用是把函数里面的值返回出来到外部
解析:第一次print出 before yield之后,遇到yield,程序终止,再次执行next(f),程序继续运行print出b的值,然后直到再次走到print(‘before yield‘)后,
程序遇到yield n,程序终止,然后再次执行next(f),以此循环......
生成器的创建方式
1. "列表"生成式(),最多支持三元表达式。 例:g2 = (i for i in range(5))
2. 函数
用生成器实现range方法
def range2(n): count = 0 while count < n: print(‘count‘, count) yield count count += 1 new_range = range2(10) r1 = next(new_range) print(r1) # 0 r2 = new_range.__next__() # 和 next(new_range)是一样的 print(r2) # 1
yield vs return
return 返回并终止函数
yield 返回数据,并冻结当前的执行过程
next 唤醒冻结的函数执行过程,继续执行,直到遇到下一个yield
函数有了yield之后
1.函数名加()就变成了一个生成器
2.return在生成器里,代表生成器的终止,直接报错
生成器和文件操作
with open(‘test.txt‘) as f: for line in f: pass
其实在for循环这个文件的时候,就是在循环一个生成器。
生成器send方法
def range2(n): count = 0 while count < n: print(‘count‘, count) sign = yield count count += 1 if sign == ‘stop‘: print(‘----sign ‘, sign) break new_range = range2(10) n1 = next(new_range) # count 0 n2 = new_range.__next__() # count 1 n3 = new_range.send(None) # count 2 这条语句等于next,因为next(或__next__)方法就是默认往生成器里面发送了一个None n4 = new_range.send(‘stop‘) # 终止,程序报错,打印----sign stop
send的作用:
1.唤醒并继续执行
2. 发送一个信息到生成器的内部
next()默认往生成器里发送一个None
迭代器
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用isinstance()判断一个对象是否是Iterable对象
from collections import Iterable print(isinstance(123, Iterable)) # False print(isinstance(‘abc‘, Iterable)) # True
只有生成器是迭代器。
from collections import Iterator li = [i for i in range(10)] print(isinstance(li, Iterator)) # False
但是可以把列表、字符串等变成迭代器
li = iter(li) print(li.__next__()) # 0 print(li.__next__()) # 1
Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。
可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,
只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
需要注意的是:
1.列表用len能知道长度,但是迭代器不能知道
2.迭代器比生成器的范围要大一些,学了面向对象后不用生成器也能next。
凡是可作用于for循环的对象都是Iterable类型;
凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
Python3的for循环本质上就是通过不断调用next()函数实现的
生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。
它不需要再像上面的类一样写__iter__()和__next__()方法了,只需要一个yiled关键字。
生成器一定是迭代器(反之不成立),因此任何生成器也是以一种懒加载的模式生成值。
以上是关于函数进阶之生成器和迭代器的主要内容,如果未能解决你的问题,请参考以下文章