# 自定义迭代器实现 class NumberIterator: def __init__(self, max_num): self.max_num = max_num self.current =0 def __iter__(self): return self def __next__(self): if self.current <self.max_num: self.current +=1 return self.current else: raise StopIteration # 使用方式 numbers =NumberIterator(5) for num in numbers: print(num)# 输出 1 2 3 4 5
但是写起来真的很麻烦啊。
生成器就不一样了 简直是懒人福音。
用yield关键字 函数就自动变成了生成器 不需要手动实现那些复杂的协议。
1 2 3 4 5 6 7 8 9 10 11 12 13
def simple_generator(n): """简单生成器示例""" for i in range(n): print(f"生成数字: {i}") yield i # 关键就在这里 print(f"数字 {i} 已被消费") # 创建生成器对象 gen = simple_generator(3) # 逐个获取值 print(next(gen)) # 生成数字: 0, 返回 0, 数字 0 已被消费 print(next(gen)) # 生成数字: 1, 返回 1, 数字 1 已被消费
你会发现 每次调用next()函数都会从上次暂停的地方继续执行。这就是生成器的核心魅力。
暂停和恢复 就像视频播放器一样。
在实际项目中 我最常用生成器来处理大文件。
比如那个50GB的日志文件 用生成器的话 内存占用几乎可以忽略不计。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
def read_large_file(file_path): """大文件逐行读取生成器""" with open(file_path,'r', encoding='utf-8')as file: for line in file: # 可以在这里做一些预处理 cleaned_line = line.strip() if cleaned_line:# 跳过空行 yield cleaned_line def process_log_data(file_path): """处理日志数据的业务逻辑""" error_count =0 for line in read_large_file(file_path): if'ERROR'in line: error_count +=1 # 这里可以做错误分析 print(f"发现错误: {line[:100]}")# 只显示前100字符 return error_count # 使用方式# total_errors = process_log_data('huge_log.txt')
这样写的好处是什么呢 内存使用量始终保持在很低的水平 不管文件多大。
生成器表达式更是简洁到爆炸。
就像列表推导式的懒加载版本 用圆括号包起来就行了。
1 2 3 4 5 6 7 8 9
# 列表推导式 - 立即创建所有元素squares_list =[x**2for x in range(1000000)]# 占用大量内存 # 生成器表达式 - 按需生成squares_gen =(x**2for x in range(1000000))# 几乎不占内存 # 实际使用场景 def find_large_squares(limit): """找出大于某个值的平方数""" squares =(x**2for x in range(10000)) return[sq for sq in squares if sq > limit] result = find_large_squares(50000) print(f"找到 {len(result)} 个符合条件的平方数")
当你需要处理大量数据但只关心其中一部分时 生成器表达式就是最佳选择。
节省内存又提升性能。
有个经常踩的坑需要提醒一下。
生成器只能迭代一次 用完就没了 就像一次性筷子。
1 2 3 4 5 6 7 8 9 10
def demo_generator(): for i in range(3): yield i gen = demo_generator() # 第一次迭代 list1 = list(gen)# [0, 1, 2] # 第二次迭代 list2 = list(gen)# [] 空列表! print(f"第一次: {list1}") print(f"第二次: {list2}")