文章目录
一、迭代器
1.什么是迭代器?
迭代(iterate)
:重复多次,就像循环那样。
我们大多只使用for循环迭代过序列
和字典
,实际上也可迭代其他对象:只要这个对象的类实现了方法__iter__
。如:
>>> class Fibs:
... def __init__(self):
... self.a=0
... self.b=1
... def __next__(self):
... self.a,self.b=self.b,self.a+self.b
...
>>> fibs=Fibs()
>>> for f in fibs:
... if f>1000:
... print(f)
... break
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Fibs' object is not iterable
上述例子中,作者没有为Fibs这个类定义__iter__这个方法,所以,产生了错误。在添加了__iter__方法后,可以看到:没有任何错误提示了。
>>> class Fibs:
... def __init__(self):
... self.a=0
... self.b=1
... def __next__(self):
... self.a,self.b=self.b,self.a+self.b
... return self.a
... def __iter__(self):
... return self #返回一个迭代器
...
>>> fibs=Fibs()
>>> for f in fibs:
... if f>1000:
... print(f)
... break
...
1597
2.迭代器的方法
迭代器包括 __next__方法:
- 若在class中漏写了next方法,for循环获取内部值的时候,会报错
TypeError: iter() returned non-iterator of type 'Fibs'
- 调用__next__方法,如果迭代器没有可供返回的值,应引发
StopIteration异常
。 xx.__next__()
==next(xx)
- 可迭代对象 → 迭代器:调用内置函数
iter()
>>> it=iter(['a','b','c'])
>>> next(it)
'a'
>>> next(it)
'b'
- 迭代器 → 可迭代对象(list为例):
>>> class TestIterator:
... value=0
... def __next__(self):
... self.value+=1
... if self.value > 10 :raise StopIteration
... return self.value
... def __iter__(self):
... return self
...
>>> ti=TestIterator()
>>> ti
<__main__.TestIterator object at 0x000001C3032524A8>
>>> next(ti)
1
>>> next(ti)
2
>>> list(ti)
[3, 4, 5, 6, 7, 8, 9, 10] #注:似乎next一个,会消失一个
>>> next(ti)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in __next__
StopIteration
二、生成器
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制
,称为生成器:generator
。
1.定义生成器
1.1 使用yield语句:
注意:
- 包含
yield语句
的函数都被称为生成器。 - generator 和 函数的执行流程不一样:
- 函数:
顺序执行
,遇到return语句/最后一行函数语句,就返回。 - generator:在每次调用
next()
的时候执行,遇到yield语句
返回,再次执行时从上次返回的 yield语句
处继续执行。
- 函数:
- yield 与 return:
- yield:应生成一个值,
- return:生成器应停止执行(即不再生成值;仅当在生成器调用return时,才能不提供任何参数)。
举个简单的例子,定义一个generator,依次返回数字1,3,5:
>>> def odd():
... print("Step 1:")
... yield 1
... print("Step 2:")
... yield 3
... print("Step 3:")
... yield 5
...
>>> o=odd()
>>> next(o)
Step 1:
1 #遇到yield 1,第一次中断
>>> next(o) #从yield 1 后面开始执行
Step 2:
3 #遇到yield 3,第二次中断
>>> next(o) #从yield 3 后面开始执行
Step 3:
5 #遇到yield 5,第二次中断
>>> next(o) #从yield 5 后面开始执行
Traceback (most recent call last): #无数据了
File "<stdin>", line 1, in <module>
StopIteration
1.2 使用生成器表达式
生成器推导
(也叫生成器表达式
)。其工作原理与列表推导相同,但不是创建一个列表(即不立即执行循环),而是返回一个生成器
,让你能够逐步执行计算。
>>> g = ((i+2)**2 for i in range(2,27)) #把一个列表生成式的[]改成()
>>> g
<generator object <genexpr> at 0x000001C303038408>
>>> next(g)
16
# 注:
#直接在一对既有的圆括号内(如在函数调用中)使用生成器推导时,无需再添加一对圆括号。换而言之,可编写下面这样非常漂亮的代码:
# sum(i ** 2 for i in range(10))
2.几种生成器
2.1 递归式生成器
>>> def flatten(nested):
... try: #递归条件(可迭代对象):遍历子序列
... for sublist in nested:
... for element in flatten(sublist):
... yield element
... except TypeError: #基线条件(只有单个元素,如一个数,迭代不了,报错)
... yield nested
...
>>> list(flatten([[[1],2],3,4,[5,[6,7]],8]))
[1, 2, 3, 4, 5, 6, 7, 8]
# 注意:这种方法存在问题:
# 如果nested是字符串或类似于字符串的对象
# 它就属于序列,因此不会引发TypeError异常
# 可你并不想对其进行迭代,所以产生了下面一种代码
改进后:
>>> def flatten(nested):
... try:
... try: nested + '' # 检查其行为是否类似于字符串,即能否与字符串拼接
... except TypeError: pass
... else: raise TypeError
... for sublist in nested:
... for element in flatten(sublist):
... yield element
... except TypeError: yield nested
...
>>> list(flatten(['foo',['bar',['baz']]]))
['foo', 'bar', 'baz']
2.2 通用生成器
生成器 = 生成器的函数 + 生成器的迭代器
生成器函数:由def语句定义的,其中包含yield。
生成器的迭代器:生成器函数返回的结果。
# 定义生成器函数
>>> def simple_generator():
yield 1
...
# 生成器函数
>>> simple_generator
<function simple_generator at 153b44>
# 生成器的迭代器
>>> simple_generator()
<generator object at 1510b0>
3.生成器的方法
3.1 send方法
在生成器开始运行后,可使用 生成器和外部之间 的通信渠道
向它提供值。这个通信渠道包含如下两个端点
。
- 外部世界:可访问
生成器的方法send
,这个方法类似于next,但它接受一个参数(要发送的“消息”,可以是任何对象)。 - 生成器:在挂起的生成器内部,yield可能用作表达式而不是语句。换而言之,当生成器重新运行时,yield返回一个值——通过send从外部世界发送的值。如果使用的是next,yield将
返回None
。
注意:
仅当生成器被挂起(即遇到第一个yield)后,使用send(而不是next)才有意义。
>>> def repeater(value):
... while True:
... new =(yield value)
... if new is not None:value=new
...
>>> r=repeater(42)
>>> next(r)
42
>>> r.send("Hello,world!")
'Hello,world!'
3.2 throw和close方法
throw:用于在生成器中(yield表达式处)引发异常,调用时可提供一个异常类型
、一个可选值
和一个traceback对象
。
close:用于停止生成器
,调用时无需提供任何参数
。
[ 参考文档 ]
- [挪]-Magnus-Lie-Hetland-Python基础教程(第3版)第九章 9.6 和 9.7
- 定义生成器的内容部分参考:https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128