Python3中的迭代器、生成器和

一、迭代器(iterator)

在Python中,for循环可以用于Python中的任何类型,包括列表、元组,字符串等等,实际上,for循环可用于任何“可迭代对象”,这其实就是迭代器
迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration。任何这类的对象在Python中都可以用for循环或其他遍历工具迭代,迭代工具内部会在每次迭代时调用next方法,并且捕捉StopIteration异常来确定何时离开。

使用迭代器一个显而易见的好处就是:每次只从对象中读取一条数据,不会造成内存的过大开销。

比如要逐行读取一个文件的内容,利用readlines()方法,我们可以这么写:

for line in open("test.txt").readlines():
    print line

这样虽然可以工作,但不是最好的方法。因为他实际上是把文件一次加载到内存中,然后逐行打印。当文件很大时,这个方法的内存开销就很大了。

利用file的迭代器,我们可以这样写:

for line in open("test.txt"):   #use file iterators
    print line

这是最简单也是运行速度最快的写法,他并没显式的读取文件,而是利用迭代器每次读取下一行。

迭代器协议

迭代器协议指的是容器类需要包含一个特殊方法
如果一个容器类提供了__iter__()方法,并且该方法能返回一个能够逐个访问容器内所有元素的迭代器,则我们说该容器类实现了迭代器协议。
python中的迭代器协议和for循环是紧密相连的

for x in something:
	print(x)

Python 处理 for 循环时,首先会调用内建函数 iter(something),它实际上会调用 something.iter(),返回 something 对应的迭代器。而后,for 循环会调用内建函数 next(),作用在迭代器上,获取迭代器的下一个元素,并赋值给 x。此后,Python 才开始执行循环体

二、生成器(constructor)

生成器函数在Python中与迭代器协议的概念联系在一起。简而言之,包含yield语句的函数会被特地编译成生成器。当函数被调用时,他们返回一个生成器对象,这个对象支持迭代器接口。函数也许会有个return语句,但它的作用是用来yield产生值的。

不像一般的函数会生成值后退出,生成器函数在生成值后会自动挂起并暂停他们的执行和状态,他的本地变量将保存状态信息,这些信息在函数恢复时将再度有效

def g(n): 
	for i in range(n): 
	yield i **2 
for i in g(5): 
	print (i)

要了解他的运行原理,我们来用next方法看看:

t = g(5) 
t.__next__()
0 
t.__next__()
1 
t.__next__()
4 
t.__next__()
9 
t.__next__()
16 
t.__next__()
Traceback (most recent call last): 
File “”, line 1, in StopIteration 

在运行完5次next之后,生成器抛出了一个StopIteration异常,迭代终止。

三、yield表达式
如果一个函数包含了yield表达式,那么这个函数就是一个生成器函数
实际上,yield表达式,只能判断函数是不是生成器函数
与普通函数不同,生成器函数被调用后,其函数体内的代码并不会立即执行,而是返回一个生成器(generator-iterator)。当返回的生成器调用成员方法时,相应的生成器函数中的代码才会执行。

def square():
	for i in range(10):
		yield i
square_gen=square()
for x in square_gen:
	print(x)

前面说到,for 循环会调用 iter() 函数,获取一个生成器;而后调用 next() 函数,将生成器中的下一个值赋值给 x;再执行循环体。因此,上述 for 循环基本等价于:

geniter=square_gen.__iter__
while True:
	x=geniter.__next__()  
	print(x)  #1
geniter.send(3)  #4
geniter.send(10)  #14

注意到,square 是一个生成器函数;作为它的返回值,square_gen 已经是一个迭代器;迭代器的 iter() 返回它自己。因此 geniter 对应的生成器函数,即是 square。

每次执行到 x = geniter.__ next __() 时,square 函数会从上一次暂停的位置开始,一直执行到下一个 yield 表达式,将 yield 关键字后的表达式列表返回给调用者,并再次暂停。注意,每次从暂停恢复时,生成器函数的内部变量、指令指针、内部求值栈等内容和暂停时完全一致。
生成器的方法
生成器有一些方法。调用这些方法可以控制对应的生成器函数;不过,若是生成器函数已在执行过程中,调用这些方法则会抛出 ValueError 异常。

generator.next():从上一次在 yield 表达式暂停的状态恢复,继续执行到下一次遇见 yield 表达式。当该方法被调用时,当前 yield 表达式的值为 None,下一个 yield 表达式中的表达式列表会被返回给该方法的调用者。若没有遇到 yield 表达式,生成器函数就已经退出,那么该方法会抛出 StopIterator 异常。
generator.send(value):和 generator.next() 类似,差别仅在与它会将当前 yield 表达式的值设置为 value。
generator.throw(type[, value[, traceback]]):向生成器函数抛出一个类型为 type 值为 value 调用栈为 traceback 的异常,而后让生成器函数继续执行到下一个 yield 表达式。其余行为与 generator.next() 类似。
generator.close():告诉生成器函数,当前生成器作废不再使用。
举例和说明
如果你看不懂生成器函数
如果你还是不太能理解生成器函数,那么大致上你可以这样去理解。

在函数开始处,加入 result = list();
将每个 yield 表达式 yield expr 替换为 result.append(expr);
在函数末尾处,加入 return result。

使用 send() 方法与生成器函数通信:

def func():
    x = 1
    while True:
        y = (yield x)
        x += y
geniter = func()
geniter.__next__()  #1
geniter.send(3)  #4
geniter.send(12)  #16

此处,生成器函数 func 用 yield 表达式,将处理好的 x 发送给生成器的调用者;与此同时,生成器的调用者通过 send 函数,将外部信息作为生成器函数内部的 yield 表达式的值,保存在 y 当中,并参与后续的处理。

这一特性是使用 yield 在 Python 中使用协程的基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值