python 基础 8 迭代器(iter)和生成器(yield)
一 生成器
从Python2.2起,生成器提供了一种简洁的方式帮助返回列表元素的函数来完成简单和有效的代码。
它基于yield指令,允许停止函数并立即返回结果。
此函数保存其执行上下文,如果需要,可立即继续执行。
1、对比range 和 xrange 的区别
>>> print range(10) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print xrange(10) xrange(10)
如上代码所示,range会在内存中创建所有指定的数字,而xrange不会立即创建,只有在迭代循环时,才去创建每个数组。
def nrange(num): temp = -1 while True: temp = temp + 1 if temp >= num: return else: yield temp
#!/usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = 'liujianzuo' def xrange(): print(11) yield 1 print(22) yield 2 print(33) yield 3 # 仅仅获取到了一个生成器 #含有yield的函数叫做生成器函数 r = xrange() #这是一个生成器 #生成器的__next__方法 ret = r.__next__() print(ret) ret = r.__next__() print(ret) ret = r.__next__() print(ret)
#!/usr/bin/env python # _*_ coding:utf-8 _*_ __author__ = 'liujianzuo' def n2(start,stop): start = start while stop: yield start start +=1 def xrange2(start,stop): obj = n2(start,stop) for i in range(start,stop): j = obj.__next__() print(j) def n1(stop): start = 0 while stop: yield start start +=1 def xrange1(stop): obj = n1(stop) for i in range(stop): j = obj.__next__() print(j) def xrange(*args): if len(args) == 1: xrange1(*args) elif len(args) == 2: xrange2(*args) else: print("参数不对 1 or 2") if __name__ == "__main__": xrange(2,11)
2、文件操作的 read 和 xreadlinex 的的区别
read会读取所有内容到内存 xreadlines则只有在循环迭代时才获取
def NReadlines(): with open('log','r') as f: while True: line = f.next() if line: yield line else: return for i in NReadlines(): print i
def NReadlines(): with open('log','r') as f: seek = 0 while True: f.seek(seek) data = f.readline() if data: seek = f.tell() yield data else: return for item in NReadlines(): print item
二 、迭代器Iterators
迭代器仅是一容器对象,它实现了迭代器协议。它有两个基本方法:
1)next方法
返回容器的下一个元素
2)__iter__方法
返回迭代器自身
迭代器可使用内建的iter方法创建,见例子:
>>> i = iter('abc') >>> i.next() 'a' >>> i.next() 'b' >>> i.next() 'c' >>> i.next() Traceback (most recent call last): File "<string>", line 1, in <string> StopIteration:
Python生成器generator之next和send运行流程
对于普通的生成器,第一个next调用,相当于启动生成器,会从生成器函数的第一行代码开始执行,直到第一次执行完yield语句(第4行)后,跳出生成器函数。
然后第二个next调用,进入生成器函数后,从yield语句的下一句语句(第5行)开始执行,然后重新运行到yield语句,执行后,跳出生成器函数,
后面再次调用next,依次类推。下面是一个列子:
1 def consumer(): 2 r = 'here' 3 for i in xrange(3): 4 yield r 5 r = '200 OK'+ str(i) 6 7 c = consumer() 8 n1 = c.next() 9 n2 = c.next() 10 n3 = c.next()
了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。其实next()和send()在一定意义上作用是相似的,区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做c.next() 和 c.send(None) 作用是一样的。需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。
下面来着重说明下send执行的顺序。当第一次send(None)(对应11行)时,启动生成器,从生成器函数的第一行代码开始执行,直到第一次执行完yield(对应第4行)后,跳出生成器函数。这个过程中,n1一直没有定义。
下面运行到send(1)时,进入生成器函数,注意这里与调用next的不同。这里是从第4行开始执行,把1赋值给n1,但是并不执行yield部分。下面继续从yield的下一语句继续执行,然后重新运行到yield语句,执行后,跳出生成器函数。
即send和next相比,只是开始多了一次赋值的动作,其他运行流程是相同的。
1 def consumer(): 2 r = 'here' 3 while True: 4 n1 = yield r 5 if not n1: 6 return 7 print('[CONSUMER] Consuming %s...' % n1) 8 r = '200 OK'+str(n1) 9 10 def produce(c): 11 aa = c.send(None) 12 n = 0 13 while n < 5: 14 n = n + 1 15 print('[PRODUCER] Producing %s...' % n) 16 r1 = c.send(n) 17 print('[PRODUCER] Consumer return: %s' % r1) 18 c.close() 19 20 c = consumer() 21 produce(c)
运行结果:
[PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK1 [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK2 [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK3 [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK4 [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK5