独行者无惧黑暗,因为他的影子,就是最锋利的剑。
学习打卡第十八天
迭代和生成是 Python 中处理序列数据的核心机制,迭代器与生成器不仅能让代码更高效,还能帮理解 Python 底层的迭代逻辑。
一、迭代器
遍历集合的高效工具
1.迭代器解释
迭代器是一个可以记住遍历位置 的对象,它从集合的第一个元素开始访问,直到所有元素被访问完结束,且只能向前遍历,不能后退。
核心特征是:
- 可通过
iter()函数创建 - 可通过
next()函数获取下一个元素 - 遍历结束时会触发
StopIteration异常
2.迭代器的基本使用
(1)创建迭代器
字符串、列表、元组等可迭代对象都能通过 iter() 函数创建迭代器:
python
# 列表创建迭代器示例
list_data = [1, 2, 3, 4]
it = iter(list_data) # 创建迭代器对象
# 用 next() 获取元素
print(next(it)) # 输出:1
print(next(it)) # 输出:2
(2)遍历迭代器
迭代器可直接用 for 循环遍历,无需手动处理 StopIteration 异常:
python
list_data = [1, 2, 3, 4]
it = iter(list_data)
for x in it:
print(x, end=" ") # 输出:1 2 3 4
也可通过 while 循环配合 try-except 捕获异常:
python
import sys
list_data = [1, 2, 3, 4]
it = iter(list_data)
while True:
try:
print(next(it)) # 逐个获取元素
except StopIteration:
sys.exit() # 遍历结束时退出
3. 自定义迭代器
通过类实现迭代器需重写两个方法:
__iter__():返回迭代器对象本身(通常返回self)__next__():返回下一个元素,遍历结束时触发StopIteration
示例:创建一个递增数字迭代器
python
class MyNumbers:
def __iter__(self):
self.a = 1 # 初始值
return self
def __next__(self):
if self.a <= 10: # 限制迭代次数
x = self.a
self.a += 1
return x
else:
raise StopIteration # 触发停止异常
# 使用自定义迭代器
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x) # 输出 1 到 10
二、生成器
简化迭代器的创建
1.生成器解释
生成器是一种特殊的迭代器 ,通过 yield 关键字定义的函数创建。它无需手动实现 __iter__() 和 __next__() 方法,就能实现迭代功能。
生成器的核心特点:
- 调用生成器函数返回的是迭代器对象
- 通过
yield语句暂停执行并返回值 - 再次调用时从上次暂停的位置继续执行
2. 生成器的基本使用
(1)简单生成器示例
python
def countdown(n):
while n > 0:
yield n # 暂停并返回当前值
n -= 1
# 创建生成器对象
generator = countdown(5)
# 逐步获取值
print(next(generator)) # 输出:5
print(next(generator)) # 输出:4
# 用 for 循环遍历剩余值
for value in generator:
print(value) # 输出:3 2 1
3. 生成器的优势
- 内存高效:无需一次性生成所有数据,按需产生值,适合处理大量数据
- 代码简洁:比自定义迭代器少写大量模板代码
- 无缝集成 :可直接用于
for循环、列表推导等迭代场景
三、迭代器与生成器的区别
| 特性 | 迭代器 | 生成器 |
|---|---|---|
| 计算方式 | 立即计算所有元素 | 按需生成,延迟计算 |
| 内存占用 | 存储全部元素 | 仅保存当前状态 |
| 遍历限制 | 可双向遍历(部分实现) | 单向遍历,状态不可逆 |
四、实战应用
案例:大文件逐行处理
- 问题:传统读取大文件易导致内存溢出。
解决方案
python
def read_large_file(file_path):
with open(file_path, 'r') as f:
# 文件对象为迭代器
for line in f:
# 逐行生成,节省内存
yield line.strip()
优势:避免一次性加载全部内容,适用于日志分析等场景。
案例:实时数据流模拟
- 问题:需要持续产出动态数据。
解决方案
python
import asyncio
import random
async def stock_price_generator(symbol):
price = 100.0
while True:
await asyncio.sleep(0.5)
price += random.uniform(-1, 1)
# 异步生成器示例
yield price
async def main():
generator = stock_price_generator("AAPL")
async for price in generator:
print(f"Current price: {price:.2f}")
if price > 110 or price < 90:
print("Price out of range!")
break
if __name__ == "__main__":
asyncio.run(main())
代码说明:
该代码实现了一个异步生成器函数 stock_price_generator,用于模拟股票价格的实时变化。
使用 asyncio.sleep 控制生成频率,每次生成间隔 0.5 秒。
通过 random.uniform 生成随机波动,使价格在一定范围内变动。
主函数中使用 async for 循环消费生成的价格数据,并在价格超出设定范围时停止。
**案例:**编写一个生成器函数,生成斐波那契数列前10项。
python
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
fib_gen = fibonacci()
for i in range(10):
print(next(fib_gen))
**案例:**使用生成器表达式过滤100以内所有3的倍数的平方数。
python
# 使用生成器表达式过滤100以内所有3的倍数的平方数
result = (x**2 for x in range(1, 101) if x % 3 == 0)
# 打印结果
print("100以内所有3的倍数的平方数:")
for num in result:
print(num, end=' ')
print() # 换行
代码说明:
该代码展示了生成器表达式与列表推导式的区别。
生成器表达式使用圆括号(),而列表推导式使用方括号[]。
生成器表达式是惰性求值,节省内存空间。
列表推导式会立即计算所有结果,占用更多内存。
案例:实现一个生成器类,模拟时间戳序列(格式:YYYY-MM-DD HH:MM:SS),起始时间为当前时间。
python
import datetime
import time
class TimestampGenerator:
"""时间戳生成器类,模拟时间戳序列"""
def __init__(self, start_time=None):
"""
初始化时间戳生成器
:param start_time: 起始时间,默认为当前时间
"""
if start_time is None:
self.current_time = datetime.datetime.now()
else:
self.current_time = start_time
def __iter__(self):
"""返回迭代器对象本身"""
return self
def __next__(self):
"""返回下一个时间戳"""
timestamp = self.current_time.strftime("%Y-%m-%d %H:%M:%S")
self.current_time += datetime.timedelta(seconds=1)
return timestamp
def next_n(self, n):
"""
获取接下来的n个时间戳
:param n: 数量
:return: 时间戳列表
"""
return [next(self) for _ in range(n)]
def main():
"""主函数演示时间戳生成器的使用"""
print("时间戳生成器演示:")
print("=" * 40)
# 创建时间戳生成器实例
generator = TimestampGenerator()
# 生成前10个时间戳
print("前10个时间戳序列:")
for i, timestamp in enumerate(generator):
print(f"{i + 1:2d}. {timestamp}")
if i >= 9:
break
print("\n" + "=" * 40)
# 使用next_n方法获取接下来的5个时间戳
print("接下来的5个时间戳:")
next_timestamps = generator.next_n(5)
for i, ts in enumerate(next_timestamps, 1):
print(f"{i:2d}. {ts}")
if __name__ == "__main__":
main()
五、总结
- 核心回顾 :
迭代器是基础协议,生成器通过yield实现惰性计算,适用于大数据、实时流等场景。 - 常见误区 :
- 生成器无法随机访问元素,需从头遍历。
- 生成器耗尽后需重新创建,避免重复使用。
- 拓展方向 :
结合装饰器优化生成器性能,或探索异步生成器在并发编程中的应用。