迭代器协议
1.迭代器协议:
·迭代器是一个对象
·迭代器可以被next()函数调用,并返回一个值
·迭代器可以被iter()函数调用,并返回迭代器自己
·连续被next()调用时返回一系列的值
·如果到了迭代的末尾,则抛出StopIteration异常
·迭代器也可以没有末尾,只要被next()调用,就一定会返回一个值
·python中,next()内置函数调用的是对象的__next__()方法
·python中,iter()内置函数调用的是__iter__()方法
·一个实现了迭代器协议的对象可以被for语句循环迭代直到终止
1)只要一个对象实现了__next__()方法,就可以被next()函数调用
class XIterator:
def __next__(self):
return 'hello world'
def main()
x_it = XIterator()
[print(next(x_it)) for i in range(3)]
#结果是打印hello world 3次
2)for语句的内部实现
for element in iterable:
#do somthing with element
#create an iterator object from that iterable
iter_obj = iter(iterable) #相当于从一个可迭代器里返回一个可迭代对象
#infinite loop
while True:
try:
#get the next item
element = next(iter_obj)
#do somthing with element 也就是说,我们平时写for语句都是从这里开始写,之前和之后的都是解释器完成的
except StopIteration:
#if StopIteration is raised , break from loop
break
说明:
for语句里用的是iterable(可迭代对象),而非iterator(迭代器)
for语句执行的第一个操作是从哪一个iterable生成一个iterator
for语句的循环体其实是靠检测StopIteration异常来中断的
要想被for语句迭代需要三个条件:iter(),next(),StopIteration
3)一个标准的迭代器的写法:有__init__,next,iter,StopIteration
class XIterator:
def __init__(self):
self.elements = list(range(5))
def __next__(self):
if self.elements:
return self.elements.pop()
else:
raise StopIteration
def __iter__(self):
return self
def main()
x_it = XIterator()
for x in x_it:
print(x)
2.Generator
·迭代器很有用,但是实现起来有些繁琐,没关系,生成器来帮你
·生成器在保持代码简洁优雅的同时,自动实现了迭代器协议
1)实现生成器的方式一:yeild Expression
def f():
yield 1
yield 2
yield 3
def main():
f_gen = f()
for x in f_gen:
print(x)
关键字yield和return的区别:yield是暂停,稍后继续,并会自动实现StopIteration;而return是直接终止
2)实现生成器方式二:Generator expression
[print(x) for x in (x ** 2 for x in range(5))]
#后面()内的表达式就是生成器表达式,即list comprehension的[]变成()就可
不要小看任何看似微小的区别:
sum([x ** 2 for x in range(1000000)])
sum(x ** 2 for x in range(1000000))
第一行:先生成一个长度为1000000的列表,然后在进行就和–>内存爆炸
第二行:其实是一个生成器,只是由于sum本身具有()而省略了(),这里的操作是聚合,即生成一个求和,生成一个求和(几乎不占内存)
也就是说,如果list存在的价值仅仅是迭代的话,用generator要好得多
3.为什么需要生成器
·相比迭代器协议,实现生成器的代码量小,可读性高
·相比在List中操作元素,直接使用生成器能节省大量内存
·有时候我们会需要写出一个无法在内存中存放的无线数据流
·可以建立生成器管道(多个生成器链式调用)
1)用生成器表示全部的斐波那契数列
def fibonacci():
temp = [1,1]
while True:
temp.append(sum(temp))
yield temp.pop(0) #yield是实现生成器的关键字
2)通过生成器管道模块化处理数据
def fibonacci():
temp = [1,1]
while True:
temp.append(sum(temp))
yield temp.pop(0)
def dataflow():
for x in fibonacci():
yield x ** 2
if __name__ == '__main__':
for x in dataflow():
print(x)
if x > 10000:
break