迭代器和生成器详解

迭代器和生成器

一、迭代器

迭代是一个重复的过程,迭代器就是每次重复都是基于上次结果而继续的。这里要记住,单纯的重复并不是迭代器。迭代器主要是用来取值的。

列表元祖等是可以利用索引进行取值,但是字典和集合是无序的,我们没有办法根据索引进行取值,要想取去字典的值就必须得用到一种不跟据索引取值的方法,这种方法就是迭代器。

1.1 可迭代对象

要研究迭代器,首先需要弄清楚什么是可迭代对象,在我们学过的数据类型中,有那么几个数据类型是可以被for循环的,而for循环我们之前说过,他就是对可迭代对象的循环。这就说明哪些数据类型是属于可迭代对象的。比如:字符串、列表、字典、集合、元祖。当然我们学的文件对象也是一种可迭代对象。

可迭代对象就是在内置的方法中有__iter__的对象都是可迭代对象。

# 可迭代对象
s = "字符串"
print(s.__iter__())  # 含有__iter__的内置方法就是可迭代对象

1.2 迭代器对象

那么什么又是迭代器对象呢?内置有_iter_、__next__的方法的对象都是迭代器对象,迭代器对象可以通过可迭代对象得到。

# 可迭代对象转为迭代器对象的方法
s = "字符串"
s_iter = s.__iter__()  # 此时的s_iter含有__next__的内置方法,就转为了迭代器对象。

# __next__:从头一次取出迭代器对象中的值。
print(s_iter.__next__())
print(s_iter.__next__())
print(s_iter.__next__())

# 如果迭代器对象中的值被取完之后依然继续取,就会报错。

迭代器对象的值一旦被取完,就会“死亡”,不能再进行取值了,在想重新取值的话,就必须重新在赋值然后重新取。

1.3 可迭代对象和迭代器区别

从定义上我们就可以看出,所有的迭代器对象都是可迭代对象,但是不是所有的可迭代对象都是迭代器对象。可迭代对象用iter之后会转化为迭代器对象,迭代器对象用iter转化依然是迭代器对象本身。

1.4 for循环的作用机制

这时候我们就可以了解一下for循环的工作机理了。

# while循环取字典的值。
dic = {"1":2,"2":2}
dic_iter = dic_iter.__iter__()

while True:
    try:
        print(dic_iter.__next__())
    except StopIteration:
        break

可以看出来,while循环也可以取出字典的值,但是比较麻烦,我们经常用的for循环更加的方便,那么for循环又是怎么工作的呢?

for k in dic:
	print(k)

# 以上述为例。
# 第一步:首先会进行dic = dic.__iter__(),将可迭代对象转化为迭代器对象
# 第二步:进行dic.__next__的调用,得到返回值给k,然后进行代码块的操作
# 第三步:循环第二步,直到出现StopIteration错误,对错误进行捕捉,退出循环。

也就是说实际上for循环就是迭代器循环。

1.5 迭代器的优点和缺点

首先说一下优点:

  1. 为序列和非序列提供了一个统一的迭代取值的方式。

  2. 惰性计算:不管迭代器对象有多大,同一时刻只有一行数据存在。

缺点也有两点:

  1. 在取得时候我们并不知道这个迭代器的长度。

  2. 取值是一次性的,过去的就让它过去,永远无法回来,除非我们在定义一个新的迭代器对象。

二、生成器

大白话:生成器就是自定义的迭代器。

生成器本身就含有iter和next的内置方法,它本身就是迭代器,那么怎么定义一个生成器呢?那就需要用到yield关键字了,yiled有以下作用

  1. yield可以暂停函数的运行,不像return,可以让函数处于运行状态且不执行代码。
  2. yield可以返回值,类似于return,其值就是生成器对象。
# next()的效果和.__next__()是一样的。
# 当生成器遇到next()的调用开始运行,遇到yield停止执行代码,返回生成器对象,等待下次next。
def func():
    print(11111)
    yield 11111
    print(22222)
    yield 22222
    
func()  # 此刻这个调用方式已经不好使了
a = func()  # 先弄一个生成器出来
b = next(a)  # 开始执行代码打印1111,在yield处暂停执行,返回11111
c = next(a)  # 继续执行2222,在yield暂停执行,返回22222
d = next(a)  # 函数体代码执行完毕,没有返回值,抛出StopIteration异常结束。

TIPS:

如若不先根据函数造一个生成器对象,即a = func()。一直使用next(func())就是在使用一个新的生成器,永远只执行第一个yield。我懵逼在这一段时间,如果你懂请忽略。

猜你喜欢

转载自www.cnblogs.com/liqianxin/p/12559863.html