1. 生成器与迭代器
生成器与迭代器是 Python 中处理序列数据的重要工具。它们通过延迟计算(lazy evaluation)机制,提高了内存效率,特别适用于处理大型数据集或无限序列。
1.1 迭代器(Iterator)
概念:
- 迭代器是一个实现了
__iter__()
和__next__()
方法的对象。 __iter__()
返回迭代器对象本身,通常用于在循环或其他迭代过程中提供一个迭代接口。__next__()
返回序列中的下一个值,当没有更多值时,抛出StopIteration
异常。
如何工作:
迭代器协议允许对象在 for
循环或其他迭代上下文中使用。每次迭代调用迭代器中的 __iter__()
方法返回 self
,并调用 __next__()
方法获取下一个元素。
原因和原理:
- 一致性要求:
根据 Python 的迭代器协议,任何可迭代对象都必须实现__iter__()
方法,并返回一个迭代器对象。这个迭代器对象必须实现__next__()
方法。在迭代器的情况下,__iter__()
方法通常返回self
,因为迭代器本身就已经实现了__next__()
方法,所以它可以自己执行迭代。 - 循环机制:
当你使用for
循环遍历一个对象时,Python 首先会调用该对象的__iter__()
方法,获得一个迭代器。然后,在每次循环时,调用该迭代器的__next__()
方法来获取下一个值。通过返回self
,迭代器对象本身既可以充当可迭代对象,也可以执行迭代操作。 - 代码简化:
返回self
可以避免创建一个新的迭代器对象,节省了资源并简化了代码。由于迭代器本身已经具备了迭代功能,所以不需要额外创建或返回其他对象。
优点:
- 节省内存:无需一次性将所有元素加载到内存中。
- 支持无限序列:如生成无限的数列。
示例:
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self # 迭代器返回自身
def __next__(self):
if self.index < len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration # 没有更多数据时抛出异常
# 使用自定义迭代器
it = MyIterator([1, 2, 3])
for item in it:
print(item)
内置迭代器:
Python 提供了许多内置迭代器,如列表、元组、字典、集合等。可以使用内置的 iter()
函数将这些对象转换为迭代器。
lst = [1, 2, 3]
it = iter(lst)
print(next(it)) # 输出 1
print(next(it)) # 输出 2
print(next(it)) # 输出 3
# print(next(it)) # 抛出 StopIteration 异常
实用案例:
- 读取大型文件:按行迭代读取,避免将整个文件内容加载到内存中。
- 数据流处理:实时处理传入数据而非一次性加载所有数据。
最佳实践:
- 自定义迭代器时,确保实现了
__iter__()
和__next__()
方法。 - 使用生成器(Generators)替代复杂的迭代器类定义,简化代码。
1.2 生成器(Generator)
概念:
- 生成器是 Python 中的一种特殊迭代器,通过
yield
关键字定义。 - 生成器函数在调用时返回一个生成器对象,而不是立即执行。
- 每次
yield
暂停函数执行,并返回一个值,保存当前状态,供下次迭代时恢复。
优点:
- 延迟计算:按需生成元素,减少内存消耗。(列表推导式会立即生成整个列表,而生成器表达式只在迭代时生成每个元素。)
- 支持无限序列:如生成斐波那契数列。
- 简化代码:比手动实现迭代器类更简洁。
示例:
def countdown(n):
while n > 0:
yield n
n -= 1
for i in countdown(5):
print(i)
生成器的工作机制:
- 生成器函数的执行在每次
yield
处暂停,等待下一次迭代继续执行。 - 状态(变量的值)在每次暂停时被保留。
生成器方法:
send(value)
: 向生成器发送值,并恢复生成器的执行。throw(type, value=None, traceback=None)
: 在生成器内部抛出异常。close()
: 终止生成器的执行。
示例:
def generator_example():
print("Generator started")
value = yield "First yield"
print(f"Received: {value}")
yield "Second yield"
gen = generator_example()
print(next(gen)) # 启动生成器,输出 "First yield"
print(gen.send("Hello")) # 发送值 "Hello",先后输出"Received: Hello","Second yield"
最佳实践:
- 使用生成器处理大型数据集或需要延迟计算的场景。
- 避免在生成器中进行复杂的状态管理,保持生成器逻辑简单明了。
1.3 生成器表达式(Generator Expressions)
概念:
- 生成器表达式类似于列表推导式,但使用圆括号
()
,返回一个生成器对象。 - 适用于需要大量元素时节省内存。
语法:
gen = (expression for item in iterable if condition)
示例:
gen = (x * x for x in range(10))
for num in gen:
print(num)
与列表推导式的比较:
- 列表推导式使用方括号
[]
,返回一个列表,立即生成所有元素。 - 生成器表达式使用圆括号
()
,返回一个生成器,按需生成元素。
内存对比:
# 列表推导式
lst = [x * x for x in range(1000000)]
# 生成器表达式
gen = (x * x for x in range(1000000))
列表 lst
占用较多内存,而生成器 gen
仅在迭代时生成每个元素,内存占用低。
最佳实践:
- 使用生成器表达式代替列表推导式,尤其是在处理大数据时。
- 若需要随机访问元素,使用列表;否则,生成器更高效。