Python3迭代器和生成器
很多朋友在初次接触 python 的 迭代器 和 生成器 时,总是不理解 生成器 和 迭代器的作用 和它们之间的关系,今天笔者来详细的讲解一下。
知识点目录:
一、迭代器
1.什么是迭代器
2.举例说明
3.StopIteration 异常的作用
4.举例说明 StopIteration 的作用
二、生成器
1.什么是生成器
2.生成器的优点
3.创建生成器的方式
4.生成器的两种方法
三、生成器 和 迭代器 的区别
一、迭代器
1.什么是迭代器
迭代是访问集合元素的一种方式,迭代器是一种可以记住遍历位置的对象。迭代器只能往前不能后退。
迭代器是可以被 next()函数调用并返回下一个值的对象
迭代器中,重点关注两种方法:
__iter__ 方法:返回一个迭代器对象,可以通过 python 内建函数 iter()调用。这个迭代器对象实现了 __next__ 方法,并通过 StopIteration 异常来标识迭代的完成。
__next__ 方法:返回下一个迭代器对象,可以通过 python 内建函数 next()调用。
2.举例说明
# 元组可迭代数据类型
i = iter((1,2,3)) # 调用 iter()方法,获得迭代器对象
print(type(i)) # 打印数据类型
print(next(i))
print(next(i)) # 调用 next()方法,获得下一个迭代器对象
print(next(i))
print(next(i))
# 结果
<class 'tuple_iterator'>
1
2
3
# 异常:
Traceback (most recent call last):
File "D:/Pycharm Perject save path/python/CeShi.py", line 19, in <module>
print(next(i))
StopIteration
# 异常原因:
元组内的元素一共是 3 个,而我调用了 4 次 next()方法,每次都会返回一个元素,
当没有元素可以返回的时候,就会自动抛出异常 StopIteration 。
一般情况下,我们使用 for 循环来遍历 数据,这样可以避免因为异常而停止程序运行。
3.StopIteration 异常的作用
StopIteration 异常 用来标记迭代的完成,防止出现无限循环的情况。当重写 迭代器类 时,我们可以在 __next__() 方法中设置当完成指定循环次数后,触发 StopIteration 异常来结束迭代。
4.举例说明 StopIteration 异常的作用
# 举例说明 StopIteration 的作用
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 4:
x = self.a
self.a += 1
return x
else:
raise StopIteration # raise 语句来触发一个异常(抛出异常)
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
# 结果
1
2
3
4
二、生成器
1.什么是生成器
在 python 中,使用了 yield 关键字的函数被称为 生成器(generator)。
和普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单的理解 生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到yield关键字 函数都会暂停运行并保存当前的所有运行信息,然后返回 yield关键字 的值,并在下一次执行 next()方法时从当前位置继续执行。
调用一个生成器,返回的是一个迭代器对象。
2.生成器的优点
1.节约内存,python 在使用生成器时对延迟操作提供了支持。所谓延迟,是指在需要的时候才产生结果,而不是立即产生结果。比如在大数据中,会使用生成器来调取数据结果而不是列表来处理数据,因为使用生成器可以 节省内存。
2.迭代到下一次的调用时,所使用的参数都是第一次所保留的。
3.创建生成器的方式
1.生成器表达式
类似于 列表推导式,将列表推导式的中括号,替换成圆括号,就是一个生成器表达式,但是生成器每次返回的结果只有一个,而不是一次构建一整个结果列表。
例子:
# 列表推导式
data = [i ** 2 for i in range(5)]
print(type(data))
print(data)
# 结果
<class 'list'>
[0, 1, 4, 9, 16]
# 生成器表达式
data = (i ** 2 for i in range(5))
print(type(data))
for i in data: # 使用for循环遍历 data
print(i)
# print(type(data))
# print(next(data))
# print(next(data))
# print(next(data))
# print(next(data))
# print(next(data))
# print(next(data)) # 获取第6次抛出 StopIteration 异常,使用 for 循环即可。
# 结果
<class 'generator'>
0
1
4
9
16
2.生成器函数
生成器函数 和 常规函数定义差不多,但是使用 yield关键字 来返回结果,而不是 return。yield 关键字每次会返回一个结果,然后将程序的运行状态挂起,以便下一次从它离开的地方继续执行。
例子:
# 生成器函数
def odd():
n = 1
while True:
yield n
n += 2
odd_num = odd() # 调用生成器函数
count = 0
for o in odd_num: # 从生成器函数中,遍历循环数据
if count >= 5:
break
print(o) # 打印遍历出来的数据
count += 1
# 结果
1
3
5
7
9
上面的生成器函数的例子中,关键字 yield 是必备的。当一个普通函数中包含 yield 关键字时,系统会默认为是一个 generator(生成器)。
4.生成器的两种方法
生成器的两种方法 __next__() 和 send()方法。
__next__()方法 和 next()方法的作用是一样的。
send()方法向生成器函数中传入数据
4.1 __next__()方法
# next() 方法示例
def odd():
n=1
while True:
yield n
n+=2
odd_num=odd()
for i in range(4):
print(next(odd_num))
# __next__() 方法示例
def odd():
n=1
while True:
yield n
n+=2
odd_num=odd()
for i in range(4):
print(odd_num.__next__())
# 结果
1
3
5
7
1
3
5
7
从上面的程序中可以看出,next(odd_num) 和 odd_num.__next__() 的作用是一样的。
4.2 send()
# send()方法示例
def fib(times):
n = 0
a, b = 0, 1
while n < times:
temp = yield b # 在 yield 前面添加一个变量
print(temp)
a, b = b, a + b
n += 1
f = fib(5)
print(f.__next__()) # 调用 __next__ 打印值
print(f.send("Super Man 超人")) # 使用send()方法传入值
print(f.send("lron Man 钢铁侠"))
# 结果
1
Super Man 超人
1
lron Man 钢铁侠
2
从上面的示例中可以看到,使用 send()方法时,必须在 yield 关键字前面添加一个变量,并打印这个变量。但是强调一点,在使用send()方法之前,必须要先使用至少一次 __next__ 方法,因为生成器不可以在使用之前导入任何非None的参数。由此可以知道,send()是用来向生成器中导入参数并返回该参数的,与该参数一起返回的还有生成器中原先保存的数据。
三、生成器 和 迭代器 的区别
生成器能够做到迭代器能做的所有事情,而且因为生成器自动创建了 iter()和 next()方法,所以会显得更加简洁一些,同时生成器相对更加高效,使用 生成器 表达式 取代 列表解析 可以节省内存。除了 创建 和 保存程序状态 的自动方法,当没有 yield 可以返回的值时,自动抛出 StopIteration 异常。
非常感谢同仁的观看.如果小编写的有问题,欢迎评论.发现问题后会第一时间修改.
祝大家工作学习顺利. ♥(ˆ◡ˆԅ)