Python迭代器与生成器

本文详细介绍了Python中的迭代器、可迭代对象和生成器。迭代器具有`__next__()`方法,是消耗性的,不能后退。可迭代对象如list、dict、str拥有`__iter__()`方法但不是迭代器。生成器是特殊的迭代器,具备send、throw、close方法,可以实现惰性求值。Python的for循环通过不断调用next()实现,生成器在并发编程中起到关键作用。
摘要由CSDN通过智能技术生成

Python迭代器与生成器

关系图

迭代器(Iterator)

定义

  • 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator,也就是说迭代器具有__next__()方法
  • 个人理解只要能使用next函数就是迭代器,所以生成器也是迭代器(其中的一种 比如正方形也是矩形

特点

  • 迭代器是消耗的,用过之后无法再使用(下面的例子可以看出第二遍循环时没有打印了)
emp = iter([1,2])
for i in emp:
    print(i)
for i in emp:
    print(i)
1
2
  • 迭代器是没法后退的,只能通过next,不断的取下一个值(所以列表,元组等不是迭代器,只是可迭代对象)
  • 迭代器有两个基本的方法:iter() 和 next()
    • iter():用来生成迭代器(由下面代码可以看到,iter接受的是可迭代的对象,所以传入迭代器也是可以的)
    • next():取迭代器的下一个值(当没有下一个元素时会抛StopIteration异常)
  • 惰性求值
# iter()
from collections.abc import Iterator
test00 = [1,2]
test01 = iter([1,2,3])
test02 = (x for x in range(2))
test03 = iter(test02)
test04 = iter(test01)
print(isinstance(test00,Iterator))
print(isinstance(test01,Iterator))
print(isinstance(test02,Iterator))
print(isinstance(test03,Iterator))
print(isinstance(test04,Iterator))
False
True
True
True
True
from collections.abc import Iterator
test00 = [1,2]
test01 = iter([1,2,3])
test02 = (x for x in range(2))
test03 = iter(test02)
test04 = iter(test01)
# 列表作为迭代器对象只有__iter__,而升级为迭代器后就有__next__方法了
print(dir(test00))
print(dir(test01))
# 迭代器对象本身中就含有__iter__和__next__
print(dir(test02))
# 迭代器对象升级为列表后,id(内存地址)是不一样的
print(id(test00))
print(id(test01))
# 迭代器再使用iter()方法,id(内存地址)是不变的
print(id(test02))
print(id(test03))
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__length_hint__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__']
['__class__', '__del__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']
4398346312
4398304560
4397514688
4397514688
# next()
test = iter([1,2])
print(next(test))
print(next(test))
print(next(test))
1
2



---------------------------------------------------------------------------

StopIteration                             Traceback (most recent call last)

<ipython-input-10-4bb69c4a1d3d> in <module>()
      3 print(next(test))
      4 print(next(test))
----> 5 print(next(test))


StopIteration: 

注意

  • 生成器都是Iterator对象,但list、dict、str虽然是Iterable(可迭代对象),却不是Iterator(迭代器)
  • 迭代器都是可迭代的,因为实现了__iter___()
  • Python3的for循环本质上就是通过不断调用next()函数实现的,把list、dict、str等Iterable变成Iterator可以使用iter()函数
  • 把一个类作为一个迭代器使用需要在类中实现两个方法 iter() 与 next()
    *1、有__next__()方法,返回容器的下一个元素或抛出StopIteration异常
    *2、有__iter__()方法,返回迭代器本身

Tips

  • 有限序列中生成无限序列
from itertools import cycle
test01 = [1,2]
test02 = cycle(test01)
print(next(test02))
print(next(test02))
print(next(test02))
print(next(test02))
print(next(test02))
  • 无限序列中生成有限序列 islice(iterable, [start,] stop [, step]) iterable 是可迭代对象,start 是开始索引,stop 是结束索引,step 是步长,start 和 step 可选
from itertools import cycle, islice
test01 = [1,2]
test02 = cycle(test01)
test03 = islice(test02,0,3)
for i in test03:
    print(i)

可迭代对象(Iterable)

定义

  • 这些可以直接作用于for 循环的对象统称为可迭代对象:Iterable
  • 个人理解:这里的迭代对象可以理解为可遍历对象,可迭代对象有__iter__()方法

类型

  1. 集合数据类型(collection):如list,tuple,dict,set,str等
  2. 生成器(generator):包括生成器和带yield的generator function
from collections.abc import Iterable
col_exp = [1,2]
gen_exp = (x for x in range(2))
print(isinstance(col_exp, Iterable))
print(isinstance(gen_exp, Iterable))

注意

  • 可迭代对象不一定是迭代器,比如集合数据类型就都不是迭代器
  • 可迭代对象实现了__iter__()
from collections.abc import Iterable
class IterTest():
    def __iter__(self):
        pass
test01 = IterTest()
print(isinstance(test01, Iterable))

生成器(generator)

定义

生成器是一种特殊的迭代器(所以任何生成器都是迭代器
Python 生成器与它的 send,throw,close 方法这篇文章采用了5中验证方式来说明生成器就是迭代器。但生成器并不完全等于迭代器,因为生成器相当于迭代器的子类,throw,close这些方法迭代器都没有。

类型

生成器有两种类型:

  1. 生成器函数:yield
  2. 生成器表达式: () eg:(x for x in range(5))
    生成器表达式与列表解析式类似,, 列表解析式: [x for x in range(5)]
# yiled
from itertools import islice
def YieldTest(n):
    while True:
        yield n
        n += 1
a = YieldTest(5)  # 生成器被实例化
for i in islice(a,0,3):
    print(i)

send,throw,close

因为生成器也是迭代器,所以next方法也是有的,用法也和迭代器一样,
但生成器也有它一些独特的方法,主要是生成器函数中使用

  • send
    • send方法相当于是与生成器通信的桥梁
    • send方法可以看作是传值+next(),所以就有传值,恢复执行,以及返回值这些操作
    • send方法入果在生成器一启动就开始使用时需要传入None,send(None)。
def sendtest():
    sendmsg = yield '这是yield'
    print(sendmsg)
    
test = SendTest()
print(test.send(None))
try:
    test.send('这是send')  # 生成器运行完会抛StopIteration
except StopIteration:
    print('生成器运行结束')
  • throw
    • 这里throw用于抛异常,有点java的感觉
    • 抛的异常在生成器暂停的地方抛出,不进行捕获的话,会传给调用方
    • 对于已经通过抛出异常而退出的生成器再使用 next(g) 会持续抛出 StopIteration 异常
    • 如果生成器退出时还没有 yield 新值,则会抛出 StopIteration 异常,因为throw是要返回值的,相当于调用了next,没有yield自然会抛StopIteration 异常
def throwtest():
    yield 1
    yield 2
    yield 3
test = throwtest()
print(next(test))
# 这里throwtest没有对异常处理,所以throwtest将异常传给了调用方,这时调用方也不会收到任何值
print(test.throw(ZeroDivisionError))
def throwtest():  
    try:
        yield 1       
    except ZeroDivisionError:
        pass
    yield 2
    yield 3
test = throwtest()
print(next(test))
# 这里throwtest对异常处理,也可以看见异常是发生在yield 1,也证明了抛出的异常在在生成器暂停的地方抛出,并且返回了下一个yield的值
print(test.throw(ZeroDivisionError))
print(next(test))
def throwtest():
    yield 1
    yield 2
    yield 3
test = throwtest()
print(next(test))
try:
    print(test.throw(ZeroDivisionError))
except ZeroDivisionError:
    print('捕获了异常')
# 对于已经通过抛出异常而退出的生成器再使用 next(g) 会持续抛出 StopIteration 异常
print(next(test))
def throwtest():  
    try:
        yield 1       
    except ZeroDivisionError:
        print('捕获异常')
    # yield 2
test = throwtest()
print(next(test))
# 因为需要返回值而已经没有yield所以抛出了StopIteration,注释掉yield 2就不会抛异常了
print(test.throw(ZeroDivisionError))
  • close(在生成器函数暂停的地方抛出一个 GeneratorExit 异常)
    • 这并不等价于generator.throw(GeneratorExit)
    • 如果生成器抛出StopIteration异常(不管是由于正常退出还是因为该生成器已经关闭),或者抛出GeneratorExit异常(不捕获该异常即可),close方法不传递该异常,直接返回到调用方。而生成器抛出的其他异常会传递给调用方。见下面第二个例子
    • GeneratorExit异常的产生意味着生成器对象的生命周期已经结束,因此生成器方法后续语句中不能再有yield,否则会产生RuntimeError。(而 throw方法是期待一个yield返回值的,如果没有,则会抛出StopIteration异常。)
    • 对于已经正常退出或者因为异常退出的生成器对象,close 方法不会进行任何操作
    • GeneratorExit异常只有在生成器对象被激活后,才有可能产生。这里激活指生成器使用next(或send)等方式
def closetest():
    yield 1
    yield 2
test = closetest()
print(next(test))
print(test.close())
# 这里可以看到,close虽然抛了GeneratorExit异常,在生成器函数中也没有对其进行捕获,但异常也没有传回给调用方,这和throw是不一样的
# 然后close是没有返回值的
1
None
def closetest2():
    try:
        yield 1
    except GeneratorExit:
        print('捕获异常')
    print('生成器结束')
test = closetest2()
print(next(test))
test.close() # 这里调用close不传递StopIteration异常
1
捕获异常
生成器结束
def closetest3():
    try:
        yield 1
    except GeneratorExit:
        print('捕获异常')
        yield 2
test = closetest3()
print(next(test))
test.close()
1
捕获异常



---------------------------------------------------------------------------

RuntimeError                              Traceback (most recent call last)

<ipython-input-13-279ecc9274b8> in <module>()
      7 test = closetest3()
      8 print(next(test))
----> 9 test.close()


RuntimeError: generator ignored GeneratorExit

yield from

这主要是在协程中用到,起到一个通信桥梁的作用
后面做并发笔记,思考协程的时候在具体写吧,可以参考下面的博客!讲的和我看的一个python高级编程中yield from的视频差不多,类似笔记挺好的

注意

生成器原理是利用了堆栈帧是分配在堆内存中 生成器原理

参考

  1. Iterables vs. Iterators vs. Generators
  2. 可迭代对象 vs 迭代器 vs 生成器
  3. Python 生成器原理详解
  4. 高效的 itertools 模块
  5. python 生成器和迭代器有这篇就够了
  6. Python 生成器与它的 send,throw,close 方法
  7. Python并发编程之深入理解yield from语法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值