一、迭代器(Iterator)
1. 什么是迭代器?
- 迭代器是一个可以在某一集合(如列表、元组等)中逐个访问元素的对象。
- 它提供了一个方法,可以记住遍历的位置,每次取出一个元素,直到所有元素被访问完毕。
- 迭代器是实现了
__iter__()
和__next__()
方法的对象。
2. 为什么需要迭代器?
- 当数据量很大时,将所有元素一次性加载到内存中会占用大量资源。
- 迭代器允许我们逐个访问元素,而不需要同时将所有元素加载到内存中,节省内存空间。
3. 如何使用迭代器?
示例:使用迭代器遍历列表
# 定义一个列表
my_list = [1, 2, 3, 4, 5]
# 获取列表的迭代器对象
my_iterator = iter(my_list)
# 逐个访问元素
print(next(my_iterator)) # 输出: 1
print(next(my_iterator)) # 输出: 2
print(next(my_iterator)) # 输出: 3
print(next(my_iterator)) # 输出: 4
print(next(my_iterator)) # 输出: 5
# 如果继续调用 next(),会引发 StopIteration 异常
# print(next(my_iterator)) # 会报错
4. 自定义迭代器
你也可以创建自己的迭代器:
class MyNumbers:
def __iter__(self):
self.a = 1 # 初始化起始值
return self
def __next__(self):
if self.a <= 5:
val = self.a
self.a += 1
return val
else:
raise StopIteration # 停止迭代
my_class = MyNumbers()
my_iter = iter(my_class)
for num in my_iter:
print(num)
# 输出: 1 2 3 4 5
二、生成器(Generator)
1. 什么是生成器?
- 生成器本质上也是一种迭代器,但更加简洁。
- 生成器是使用了
yield
关键字的函数,调用该函数并不会执行函数体,而是返回一个生成器对象。 - 每次调用生成器的
__next__()
方法时,函数会执行到下一个yield
,并返回相应的值,函数的状态会被保留,下一次从上次暂停的地方继续执行。
2. 为什么需要生成器?
- 生成器使得编写迭代器更加容易。
- 它可以在循环中逐个产生值,而不需要像迭代器那样编写完整的类。
- 生成器可以节省内存,因为它们不会一次性将所有计算结果存入内存,而是按需生成。
3. 如何使用生成器?
示例:创建一个简单的生成器函数
def my_generator():
print("第一次调用")
yield 1
print("第二次调用")
yield 2
print("第三次调用")
yield 3
gen = my_generator()
print(next(gen)) # 输出: 第一次调用 \n 1
print(next(gen)) # 输出: 第二次调用 \n 2
print(next(gen)) # 输出: 第三次调用 \n 3
# 如果继续调用 next(),会引发 StopIteration 异常
# print(next(gen)) # 会报错
示例:使用生成器生成斐波那契数列
def fibonacci(n):
a, b = 0, 1
count = 0
while count < n:
yield a
a, b = b, a + b
count += 1
fib = fibonacci(5)
for num in fib:
print(num)
# 输出: 0 1 1 2 3
4. 生成器表达式
除了生成器函数外,Python 还提供了生成器表达式,更加简洁:
gen_exp = (x * x for x in range(5))
for num in gen_exp:
print(num)
# 输出: 0 1 4 9 16
三、迭代器和生成器的区别
- 迭代器是实现了迭代协议的对象,需要实现
__iter__()
和__next__()
方法,使用起来稍显复杂。 - 生成器是使用
yield
关键字的函数,自动实现了迭代器协议,编写起来更为简洁。
四、总结
- 迭代器允许我们在不加载整个可迭代对象的情况下,逐个访问其中的元素,节省内存空间。
- 生成器使得创建迭代器变得简单,不需要编写复杂的类和方法,使用
yield
就可以实现。 - 当处理大量数据或需要返回一个序列但是不想一次性占用大量内存时,生成器是非常有用的工具。