(7)100天python从入门到拿捏《迭代器和生成器》
迭代器和生成器
在 Python 中,迭代器和生成器是非常重要的概念。它们使得 Python 的循环结构更加简洁高效,尤其在处理大量数据时,它们可以大大减少内存的消耗。下面我会详细介绍迭代器和生成器的定义、用法、以及它们的区别和关系。
文章目录
- 迭代器和生成器
- 1. 迭代器
- 定义
- 特点
- 迭代器的工作原理
- 创建迭代器
- 自定义迭代器
- 2. 生成器
- 定义
- 特点
- 创建生成器
- 使用 `for` 循环遍历生成器
- 3. 生成器表达式
- 4. **迭代器与生成器的区别**
- 5. **生成器的性能优势**
- 生成大量数据时的内存优势
- 6. **生成器的 `Send()` 和 `Close()` 方法**
- `send()` 方法
- `close()` 方法
- 总结
1. 迭代器
定义
迭代器是一个对象,它实现了 __iter__()
和 __next__()
方法,使得对象可以在循环中逐个返回其元素。迭代器用于遍历一个集合数据,比如列表、元组、字典等。
特点
- 迭代器在内存中逐个生成元素,不会一次性将所有数据存储在内存中。
- 迭代器只能向前遍历,不能倒回。
- 迭代器提供了 惰性求值,即只有在需要时才会生成下一个元素。
迭代器的工作原理
- 通过
__iter__()
方法返回迭代器对象本身。 - 通过
__next__()
方法返回下一个元素。 - 当没有元素时,
__next__()
会引发StopIteration
异常。
创建迭代器
可以通过可迭代对象的 iter()
函数将其转换为迭代器。
# 创建一个迭代器
my_list = [1, 2, 3]
iterator = iter(my_list)print(next(iterator)) # 输出:1
print(next(iterator)) # 输出:2
print(next(iterator)) # 输出:3
# 如果再次调用next(),将抛出 StopIteration 异常
# print(next(iterator)) # StopIteration
自定义迭代器
可以通过定义一个类,并实现 __iter__()
和 __next__()
方法来创建自己的迭代器。
class MyIterator:def __init__(self, start, end):self.current = startself.end = enddef __iter__(self):return selfdef __next__(self):if self.current >= self.end:raise StopIterationself.current += 1return self.current - 1# 使用自定义的迭代器
iterator = MyIterator(1, 4)
for value in iterator:print(value)
输出:
1
2
3
2. 生成器
定义
生成器是一个特殊类型的迭代器。它是通过 yield
关键字定义的函数(也叫生成器函数)。与普通函数不同,生成器函数在执行时会暂停并记住当前位置,直到下次调用生成器时再继续执行。
特点
- 生成器使用了 惰性求值,在需要时才生成一个元素,因此它们是高效的内存利用方式。
- 生成器函数通过
yield
语句逐个返回数据,每次调用next()
都会继续从上次停止的地方开始。 - 生成器会自动实现迭代器的协议,因此可以像迭代器一样使用
for
循环进行遍历。
创建生成器
生成器是通过带有 yield
的函数定义出来的。每当 yield
被执行时,函数暂停并返回一个值,之后可以通过调用 next()
恢复执行并返回下一个值。
def my_generator():yield 1yield 2yield 3gen = my_generator()
print(next(gen)) # 输出:1
print(next(gen)) # 输出:2
print(next(gen)) # 输出:3
# 如果再次调用next(),将抛出 StopIteration 异常
# print(next(gen)) # StopIteration
使用 for
循环遍历生成器
for value in my_generator():print(value)
输出:
1
2
3
3. 生成器表达式
生成器表达式是一种简洁的生成器定义方式,它类似于列表推导式,但返回的是生成器对象而不是一个列表。
语法:
(expression for item in iterable if condition)
案例
gen_exp = (x ** 2 for x in range(5))
print(next(gen_exp)) # 输出:0
print(next(gen_exp)) # 输出:1
print(next(gen_exp)) # 输出:4
4. 迭代器与生成器的区别
特性 | 迭代器 (Iterator) | 生成器 (Generator) |
---|---|---|
定义方式 | 通过实现 __iter__() 和 __next__() 方法定义 | 通过 yield 关键字或生成器表达式定义 |
内存管理 | 一次性将所有元素加载到内存中 | 按需生成元素,内存占用低 |
实现复杂度 | 需要定义 __iter__() 和 __next__() 方法 | 通过函数和 yield 关键字实现,代码更简洁 |
惰性求值 | 是 | 是 |
用法 | 手动实现迭代器协议 | 更加简洁和直观,适用于生成大量数据 |
是否能迭代 | 是 | 是 |
是否可以重新迭代 | 不能重新开始迭代(除非重新创建迭代器对象) | 不能重新开始迭代(除非重新创建生成器对象) |
5. 生成器的性能优势
生成器的主要优势是 内存效率。它们在每次迭代时生成一个值,并将其返回,而不是一次性将所有数据加载到内存中。这在处理大量数据时尤为重要,特别是当数据量非常大,甚至超出内存容量时,生成器提供了一个很好的解决方案。
生成大量数据时的内存优势
当你要处理一个包含 10 亿数字的列表,如果使用普通列表,你的程序可能会因为内存不足而崩溃。而使用生成器,你的程序只会按需生成每个元素,不会占用太多内存。
def large_data_generator():for i in range(1000000000):yield i# 这个生成器不会一次性将所有 10 亿个数字存储在内存中
gen = large_data_generator()
6. 生成器的 Send()
和 Close()
方法
生成器不仅仅能使用 yield
来生成值,还支持 send()
和 close()
方法,用于向生成器传递数据或关闭生成器。
send()
方法
send()
方法用于向生成器传递值并恢复生成器的执行,生成器会执行到下一个 yield
语句,并将该值返回。
案例
def generator():result = yield 1 # 暂停并返回 1yield result # 返回通过 send() 传入的值gen = generator()
print(next(gen)) # 输出:1
print(gen.send(10)) # 输出:10
close()
方法
close()
方法用于关闭生成器,停止生成器的执行。
案例
def generator():yield 1yield 2gen = generator()
print(next(gen)) # 输出:1
gen.close() # 关闭生成器
总结
- 迭代器 是一种可以用于遍历的对象,通过实现
__iter__()
和__next__()
方法来定义。 - 生成器 是一种特殊类型的迭代器,通过使用
yield
关键字定义,具备惰性求值的特点,更加节省内存。 - 生成器表达式 是一种更简洁的定义生成器的方式,类似于列表推导式,但返回的是生成器。
- 生成器的 内存优势 在于它们只在需要时生成数据,而不一次性加载所有数据,这使得它们特别适合处理大量数据。
python学习专栏导航
(1)100天python从入门到拿捏《Python 3简介》
(2)100天python从入门到拿捏《python应用前景》
(3)100天python从入门到拿捏《数据类型》
(4)100天python从入门到拿捏《运算符》
(5)100天python从入门到拿捏《流程控制语句》
(6)100天python从入门到拿捏《推导式》