Python迭代器与生成器
迭代器
迭代器是访问集合元素的一种方式。
迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器的两个基本方法:iter()
和next()
字符串、列表或元组对象都可用于创建迭代器。
文件不需要创建迭代器,文件本身就是迭代器。
迭代器对象不要求事先准备好整个迭代过程中的所有元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或者之后,元素可以不存在或者被销毁。
>>> list = [1,2,3,4]
>>> list_iter = iter(list)
>>> list_iter.next()
1
>>> list_iter.next()
2
>>> list_iter.next()
3
>>> list_iter.next()
4
当list没有next之后,抛出StopIteration异常。
>>> list_iter.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
生成器
生成器是一种特殊的迭代器。生成器一定是迭代器,迭代器不一定是生成器。
它不需要再像上面的类一样写__iter__()
和__next__()
方法了,只需要一个yiled关键字。
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
# coding=utf-8
from __future__ import print_function
import sys
def fibonacci(n): # 生成器函数 - 斐波那契
a, b, counter = 0, 1, 0
while True:
if (counter > n):
return
yield a
a, b = b, a + b
counter += 1
f = fibonacci(10) # f 是一个迭代器,由生成器返回生成
print (f)
while True:
try:
print (next(f), end=" ")
except StopIteration: #抛出异常
sys.exit()
运行结果
<generator object fibonacci at 0x00000000030511F8>
0 1 1 2 3 5 8 13 21 34 55
问: 将列表生成式中[]改成() 之后数据结构是否改变?
答案:是,从列表变为生成器
通过列表生成式,可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。
此外,如果创建一个包含百万元素的列表,不仅是占用很大的内存空间,如:我们只需要访问前面的几个元素,后面大部分元素所占的空间都是浪费的。因此,没有必要创建完整的列表(节省大量内存空间)。
在Python中,可以采用生成器:边循环,边计算的机制—>generator。
>>> list = [i for i in range(5)]
>>> list
[0, 1, 2, 3, 4]
>>> type(list)
<type 'list'>
>>> generator = (i for i in range(5))
>>> generator
<generator object <genexpr> at 0x0000000003583240>
>>> type(generator)
<type 'generator'>
生成器示例代码
示例01
# -*- coding: utf-8 -*-
import sys
def out_money(totle):
while totle > 0:
totle -= 1
yield 1 # yield 返回一个值
ATM = out_money(3) #ATM generator中的值为:1 1 1,即如果存在next,next(ATM)=1
while True:
try:
print("取到钱 %s 万" % next(ATM))
print("花掉花掉!")
except StopIteration:
print ("没钱了")
sys.exit()
运行结果
取到钱 1 万
花掉花掉!
取到钱 1 万
花掉花掉!
取到钱 1 万
花掉花掉!
没钱了
示例02
# coding=utf-8
import time
def consumer(name):
print("%s 准备吃包子了!" %name)
while True:
baozi = yield # yield 通过 send()方法接收值
print("包子[%s]来了,被[%s]吃了" %(baozi,name))
def producer(name):
c1 = consumer('A')
c2 = consumer('B')
c3 = consumer('C')
next(c1)
next(c2)
next(c3)
print("%s 开始准备做包子了!" % name)
for i in range(5):
time.sleep(1)
print('做了3个包子')
c1.send(i)
c2.send(i)
c3.send(i)
producer('hh')
运行结果
A 准备吃包子了!
B 准备吃包子了!
C 准备吃包子了!
hh 开始准备做包子了!
做了3个包子
包子[0]来了,被[A]吃了
包子[0]来了,被[B]吃了
包子[0]来了,被[C]吃了
做了3个包子
包子[1]来了,被[A]吃了
包子[1]来了,被[B]吃了
包子[1]来了,被[C]吃了
做了3个包子
包子[2]来了,被[A]吃了
包子[2]来了,被[B]吃了
包子[2]来了,被[C]吃了
做了3个包子
包子[3]来了,被[A]吃了
包子[3]来了,被[B]吃了
包子[3]来了,被[C]吃了
做了3个包子
包子[4]来了,被[A]吃了
包子[4]来了,被[B]吃了
包子[4]来了,被[C]吃了