python 迭代器和生成器

参考python 生成器和迭代器有这篇就够了
python中迭代器和生成器

一、可迭代对象和迭代器定义

  • 可迭代对象(Iterable):内部包含__iter__(),可以直接作用于for 循环
  • 迭代器(Iterator): 内部含有__iter__()和__next__(),可以被next()函数调用并不断返回下一个值。iter() 方法返回一个特殊的迭代器对象, 这个迭代器对象实现了 next() 方法并通过 StopIteration 异常标识迭代的完成。

二、判断一个对象是否为Iterable(可迭代对象)和Iterator(迭代器)

1、可以使用isinstance()判断一个对象是否为可Iterable可迭代对象

from collections import Iterable

print(isinstance([], Iterable))

print(isinstance({}, Iterable))

print(isinstance("abc", Iterable))

执行结果

True
True
True

2、可以使用isinstance()判断一个对象是否是Iterator迭代器

from collections import Iterator
print(isinstance((x for x in range(10)), Iterator))

print(isinstance([], Iterator))

print(isinstance({}, Iterator))

print(isinstance('abc', Iterator))

执行结果

True
False
False
False

3、类型示例

s='hello'     #字符串是可迭代对象,但不是迭代器
l=[1,2,3,4]     #列表是可迭代对象,但不是迭代器
t=(1,2,3)       #元组是可迭代对象,但不是迭代器
d={'a':1}        #字典是可迭代对象,但不是迭代器
set={1,2,3}     #集合是可迭代对象,但不是迭代器
# *************************************
f=open('test.txt') #文件是可迭代对象,是迭代器

list、dict、str虽然是Iterable(可迭代对象),却不是Iterator(迭代器),生成器都是Iterator(迭代器)。

4、Python3的for循环本质上就是通过不断调用next()函数实现的

lst=[1, 2, 3, 4, 5]
for x in lst:
    pass

实际上完全等价于

lst=[1, 2, 3, 4, 5]
# 首先获得Iterator对象:
it = lst.__iter__()
# 循环:
while True:
    try:
        # 获得下一个值:
        x = next(it)
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

5、小结

  • 凡是可作用于for循环的对象都是Iterable类型;
  • 凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
  • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数来获得一个Iterator对象。

三、生成器定义

1、定义

  • 在 Python 中,使用了 yield 的函数被称为生成器(generator)。

  • 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。调用一个生成器函数,返回的是一个迭代器对象。

  • 在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。

2、生成器的三种创建办法:

  • 通过生成器函数
  • 通过生成器表达式创建生成器
  • 通过数据转换

3、那么,生成器是怎么调用执行的呢?只需要了解下面几条规则即可:

  • 当生成器被调用的时候,函数体的代码不会被执行,而是会返回一个迭代器,其实,生成器函数返回生成器的迭代器。 “生成器的迭代器”这个术语通常被称作”生成器”。要注意的是生成器就是一类特殊的迭代器。作为一个迭代器,生成器必须要定义一些方法,其中一个就是next()。如同迭代器一样,我们可以使用next()函数来获取下一个值。需要明白的是,这一切都是在yield内部实现的。

  • 当next()方法第一次被调用的时候,生成器函数才开始执行,执行到yield语句处停止
    next()方法的返回值就是yield语句处的参数(yielded value)
    当继续调用next()方法的时候,函数将接着上一次停止的yield语句处继续执行,并到下一个yield处停止;如果后面没有yield就抛出StopIteration异常。

  • 每调用一次生成器的next()方法,就会执行生成器中的代码,知道遇到一个yield或者return语句。yield语句意味着应该生成一个值(在上面已经解释清楚)。return意味着生成器要停止执行,不在产生任何东西。

  • 生成器的编写方法和函数定义类似,只是在return的地方改为yield。生成器中可以有多个yield。当生成器遇到一个yield时,会暂停运行生成器,返回yield后面的值。当再次调用生成器的时候,会从刚才暂停的地方继续运行,直到下一个yield。生成器自身又构成一个循环器,每次循环使用一个yield返回的值。

四、生成器函数

1、常规函数的定义,使用yield语句而不是return语句返回结果。yield语句语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

def test():
    print(123)   #当生成器函数被调用时,函数体的代码不会被执行,这里的print(123),会在第一次next时才执行
    yield 1
    yield 2
    yield 3
    
obj=test()
print(obj)

print(obj.__next__())
print(obj.__next__())
print(obj.__next__())
#print(obj.__next__()) #报错,超出下标索引

执行结果
在这里插入图片描述

2、对yield的总结

(1)通常的for…in…循环中,in后面是一个数组,这个数组就是一个可迭代对象,类似的还有链表,字符串,文件。他可以是a = [1,2,3],也可以是a = [x*x for x in range(3)]。

它的缺点也很明显,就是所有数据都在内存里面,如果有海量的数据,将会非常耗内存。

(2)生成器是可以迭代的,但是只可以读取它一次。因为用的时候才生成,比如a = (x*x for x in range(3))。!!!注意这里是小括号而不是方括号。

(3)生成器(generator)能够迭代的关键是他有next()方法,工作原理就是通过重复调用next()方法,直到捕获一个异常。

(4)带有yield的函数不再是一个普通的函数,而是一个生成器generator,可用于迭代

(5)yield是一个类似return 的关键字,迭代一次遇到yield的时候就返回yield后面或者右面的值。而且下一次迭代的时候,从上一次迭代遇到的yield后面的代码开始执行

(6)yield就是return返回的一个值,并且记住这个返回的位置。下一次迭代就从这个位置开始。

(7)带有yield的函数不仅仅是只用于for循环,而且可用于某个函数的参数,只要这个函数的参数也允许迭代参数。

(8)send()和next()的区别就在于send可传递参数给yield表达式,这时候传递的参数就会作为yield表达式的值,而yield的参数是返回给调用者的值,也就是说send可以强行修改上一个yield表达式值。

(9)send()和next()都有返回值,他们的返回值是当前迭代遇到的yield的时候,yield后面表达式的值,其实就是当前迭代yield后面的参数。

(10)第一次调用时候必须先next()或send(),否则会报错,send后之所以为None是因为这时候没有上一个yield,所以也可以认为next()等同于send(None)

五、生成器表达式

1、使用列表推导式,将会一次产生所有结果:
语法 [ 最终结果(变量) for 变量 in 可迭代对象 ]

squares = [x**2 for x in range(5)]
print(squares)

执行结果

[0, 1, 4, 9, 16]

2、将列表推导的中括号,替换成圆括号,就是一个生成器表达式:

squares = (x ** 2 for x in range(5))
print(squares)   #<generator object <genexpr> at 0x0000026D0361D048>

#next(squares)和squares.__next__()是等价的
print(next(squares))        #0
print(next(squares))        #1
print(squares.__next__())   #4
print(squares.__next__())   #9
print(squares.__next__())   #16
print(squares.__next__()) #报错,超出索引下标

执行结果
在这里插入图片描述
3、两者转换

print(list(x**2 for x in range(5)))

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值