Python 迭代机制指南:可迭代对象、不可迭代对象、迭代器与生成器
目录
可迭代对象(Iterables)
定义
可迭代对象是指可以被迭代(逐一返回其中的元素)的任何对象。在Python中,任何实现了 __iter__() 方法或 __getitem__() 方法的对象都是可迭代的。
特点
- 可以使用
for循环遍历 - 可以用于列表推导式和生成器表达式中
- 可以传递给内置函数
iter()来获取迭代器 - 支持
in操作符来检查成员关系
常见的可迭代对象
- 列表 (list)
- 元组 (tuple)
- 字符串 (str)
- 字典 (dict)
- 集合 (set)
- 文件对象
- 自定义实现了
__iter__()或__getitem__()方法的类
示例代码
# 常见可迭代对象的使用
my_list = [1, 2, 3, 4, 5]
my_tuple = (10, 20, 30)
my_string = "Hello"
my_dict = {"a": 1, "b": 2, "c": 3}
my_set = {100, 200, 300}
# 使用for循环遍历
for item in my_list:
print(item)
# 在列表推导式中使用
squared = [x**2 for x in my_tuple]
print(squared) # [100, 400, 900]
# 使用in操作符
print('H' in my_string) # True
# 迭代字典
for key in my_dict: # 默认迭代键
print(key, my_dict[key])
# 使用字典的方法获取不同迭代视图
print(list(my_dict.keys())) # ['a', 'b', 'c']
print(list(my_dict.values())) # [1, 2, 3]
print(list(my_dict.items())) # [('a', 1), ('b', 2), ('c', 3)]
创建自定义可迭代对象
class CustomIterable:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
# 返回一个迭代器对象
return CustomIterator(self.start, self.end)
class CustomIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
# 迭代器本身也是可迭代的
return self
def __next__(self):
if self.current < self.end:
value = self.current
self.current += 1
return value
else:
# 迭代结束时抛出StopIteration异常
raise StopIteration
# 使用自定义可迭代对象
my_iterable = CustomIterable(1, 5)
for i in my_iterable:
print(i) # 输出: 1, 2, 3, 4
不可迭代对象(Non-iterables)
定义
不可迭代对象是指不能被直接迭代的对象。这些对象没有实现 __iter__() 方法,也没有按序号索引的 __getitem__() 方法。
特点
- 不能直接用于
for循环 - 不能用于列表推导式
- 不能用于生成器表达式
- 不能传递给
iter()函数 - 不支持
in操作符检查成员关系
常见的不可迭代对象
- 数字 (int, float, complex)
- 布尔值 (bool)
- NoneType (None)
- 函数和方法
- 模块
- 没有实现迭代协议的自定义类
示例代码
# 不可迭代对象示例
number = 42
boolean = True
none_value = None
# 尝试迭代这些对象会引发TypeError
try:
for i in number:
print(i)
except TypeError as e:
print(f"错误: {e}") # 输出: 错误: 'int' object is not iterable
# 判断对象是否可迭代
def is_iterable(obj):
try:
iter(obj)
return True
except TypeError:
return False
print(is_iterable([1, 2, 3])) # True
print(is_iterable("Hello")) # True
print(is_iterable(42)) # False
print(is_iterable(None)) # False
将不可迭代对象转换为可迭代
# 将不可迭代对象包装成可迭代对象
class SingleItemIterable:
def __init__(self, item):
self.item = item
self.done = False
def __iter__(self):
return self
def __next__(self):
if not self.done:
self.done = True
return self.item
raise StopIteration
# 现在可以迭代任何对象了
for i in SingleItemIterable(42):
print(i) # 输出: 42
迭代器(Iterators)
定义
迭代器是一个表示数据流的对象,它实现了迭代器协议,即 __iter__() 和 __next__() 两个方法。迭代器会记住它的迭代状态。
特点
- 实现了
__iter__()和__next__()方法 - 可以通过
next()函数获取下一个元素 - 当没有更多元素时抛出
StopIteration异常 - 是惰性的,只在需要时才计算下一个元素
- 一次性使用,遍历完后不能重新开始
- 迭代器本身也是可迭代对象
创建迭代器
# 使用内置iter()函数创建迭代器
my_list = [1, 2, 3, 4, 5]
iterator = iter(my_list)
# 使用next()获取下一个元素
print(next(iterator)) # 1
print(next(iterator)) # 2
# 使用for循环迭代剩余元素
for item in iterator:
print(item) # 3, 4, 5
# 注意:迭代器已经遍历完毕,再次使用next()会抛出StopIteration异常
try:
print(next(iterator))
except StopIteration:
print("迭代器已耗尽")
自定义迭代器
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
self.current -= 1
return self.current + 1
# 使用自定义迭代器
countdown = CountDown(5)
for num in countdown:
print(num) # 输出: 5, 4, 3, 2, 1
可迭代对象与迭代器的区别
-
可迭代对象:
- 实现了
__iter__()方法,用于返回迭代器 - 可以多次迭代
- 例如:列表、元组、字符串等
- 实现了
-
迭代器:
- 实现了
__iter__()和__next__()方法 - 跟踪迭代的状态
- 是一次性的,迭代完毕后不能重新开始
- 是惰性的,按需生成元素
- 实现了
# 演示区别
my_list = [1, 2, 3] # 可迭代对象
# 可以多次迭代可迭代对象
for i in my_list:
print(i, end=' ') # 1 2 3
print()
for i in my_list:
print(i, end=' ') # 1 2 3
print()
# 而迭代器是一次性的
iterator = iter(my_list)
for i in iterator:
print(i, end=' ') # 1 2 3
print()
for i in iterator:
print(i, end=' ') # 不会输出任何内容,因为迭代器已耗尽
print("(迭代器已耗尽)")
生成器(Generators)
定义
生成器是一种特殊的迭代器,使用函数和yield语句创建。生成器可以懒惰地生成值,避免一次性在内存中创建整个序列。
特点
- 使用
yield语句而不是return - 自动实现迭代器协议(
__iter__和__next__) - 保存函数执行状态,下次调用时从上次暂停的地方继续
- 惰性计算,按需生成元素
- 内存效率高,适合处理大数据集和无限序列
- 一次性使用,不能重置
生成器函数
def count_up_to(max):
count = 1
while count <= max:
yield count
count += 1
# 使用生成器函数
counter = count_up_to(5)
print(next(counter)) # 1
print(next(counter)) # 2
# 在for循环中使用
for num in counter:
print(num, end=' ') # 3 4 5
print()
生成器表达式
# 生成器表达式(类似列表推导式,但使用圆括号)
squares_gen = (x**2 for x in range(1, 6))
# 遍历生成器
for square in squares_gen:
print(square, end=' ') # 1 4 9 16 25
print()
生成器的高级特性
1. send() 方法:向生成器发送值
def echo_generator():
value = yield "Ready"
while True:
value = yield f"Echo: {value}"
gen = echo_generator()
print(next(gen)) # Ready(启动生成器)
print(gen.send("Hello")) # Echo: Hello
print(gen.send(42)) # Echo: 42
2. throw() 方法:向生成器内部抛出异常
def number_generator():
try:
yield 1
yield 2
yield 3
except ValueError:
yield 'Exception handled!'
gen = number_generator()
print(next(gen)) # 1
print(gen.throw(ValueError)) # Exception handled!
3. close() 方法:关闭生成器
def countdown_generator():
print("Starting countdown")
try:
yield 3
yield 2
yield 1
finally:
print("Generator closed!")
gen = countdown_generator()
print(next(gen)) # Starting countdown, 3
gen.close() # Generator closed!
4. yield from:简化生成器代码的语法
def subgenerator():
yield 1
yield 2
yield 3
def main_generator():
yield 'Start'
yield from subgenerator() # 委托给子生成器
yield 'End'
for item in main_generator():
print(item) # Start, 1, 2, 3, End
无限生成器示例
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 获取斐波那契数列的前10个数
fib = fibonacci()
for _ in range(10):
print(next(fib), end=' ') # 0 1 1 2 3 5 8 13 21 34
print()
实际应用
处理大文件
def read_large_file(file_path):
with open(file_path, 'r') as f:
for line in f: # 文件对象是可迭代的,每次产生一行
yield line.strip()
# 分析一个大日志文件(不必一次性加载整个文件)
def count_errors(log_file):
error_count = 0
for line in read_large_file(log_file):
if "ERROR" in line:
error_count += 1
return error_count
# 假设使用
# print(count_errors("huge_log_file.txt"))
数据流处理管道
def read_data(file_path):
with open(file_path, 'r') as f:
for line in f:
yield line.strip()
def parse_data(lines):
for line in lines:
try:
yield int(line)
except ValueError:
continue
def filter_data(numbers):
for num in numbers:
if num > 0:
yield num
def process_data(file_path):
lines = read_data(file_path)
numbers = parse_data(lines)
positive_numbers = filter_data(numbers)
return positive_numbers
# 使用处理管道
# for item in process_data("data.txt"):
# print(item)
自定义序列迭代器
class DateRange:
"""日期范围迭代器,产生两个日期之间的所有日期"""
import datetime
def __init__(self, start_date, end_date):
self.start_date = start_date
self.end_date = end_date
def __iter__(self):
current_date = self.start_date
while current_date <= self.end_date:
yield current_date
current_date += self.datetime.timedelta(days=1)
# 示例使用
# from datetime import date
# start = date(2023, 1, 1)
# end = date(2023, 1, 10)
# for d in DateRange(start, end):
# print(d)
性能对比
内存使用
下面是列表方式和生成器方式处理数据的内存使用对比:
import sys
# 列表方式(一次性加载到内存)
def get_squares_list(n):
return [x**2 for x in range(n)]
# 生成器方式(按需生成)
def get_squares_generator(n):
for x in range(n):
yield x**2
# 比较内存占用
list_size = sys.getsizeof(get_squares_list(1000000))
gen_size = sys.getsizeof(get_squares_generator(1000000))
print(f"列表占用内存: {list_size} 字节")
print(f"生成器占用内存: {gen_size} 字节")
# 生成器占用的内存通常远小于列表
执行速度
import time
def measure_time(func, *args):
start = time.time()
result = func(*args)
if not isinstance(result, (list, tuple)):
# 如果是生成器,需要遍历以确保执行
for _ in result:
pass
end = time.time()
return end - start
def sum_squares_list(n):
return sum([x**2 for x in range(n)])
def sum_squares_generator(n):
return sum(x**2 for x in range(n))
n = 10000000
list_time = measure_time(sum_squares_list, n)
gen_time = measure_time(sum_squares_generator, n)
print(f"列表方式耗时: {list_time:.5f} 秒")
print(f"生成器方式耗时: {gen_time:.5f} 秒")
# 对于简单操作,列表推导式有时更快
# 对于复杂或内存密集型操作,生成器通常更快
最佳实践
何时使用迭代器/生成器
- 处理大数据集:当数据集太大,无法一次性加载到内存中时
- 处理无限序列:例如实时数据流或需要按需生成的序列
- 惰性求值:当计算成本高,且可能不需要所有结果时
- 实现数据处理管道:将处理步骤分解为独立的生成器函数
何时使用列表/其他容器
- 需要多次迭代:当数据需要被多次遍历时
- 需要随机访问:当需要通过索引访问元素时
- 数据集较小:当内存不是问题时
- 需要使用特定的容器方法:如排序、计数等
迭代器和生成器的通用模式
-
链式处理:将多个生成器连接形成处理管道
def process_pipeline(data): step1 = (transform1(x) for x in data) step2 = (transform2(x) for x in step1 if condition(x)) return step2 -
批处理模式:以固定大小的批次处理数据
def batch_process(items, batch_size=100): batch = [] for item in items: batch.append(item) if len(batch) >= batch_size: yield batch batch = [] if batch: # 处理最后一个不完整的批次 yield batch -
无限生成器与取样模式:从无限序列中获取有限样本
def take(n, iterable): """从迭代器中获取前n个元素""" return list(islice(iterable, n))
避免常见的错误
-
多次使用同一个迭代器:迭代器是一次性的,使用后就耗尽了
# 错误示例 iterator = iter([1, 2, 3]) list(iterator) # [1, 2, 3] list(iterator) # [],迭代器已耗尽 # 正确做法 my_list = [1, 2, 3] list(iter(my_list)) # [1, 2, 3] list(iter(my_list)) # [1, 2, 3],每次创建新的迭代器 -
无意中消耗迭代器:调试时打印迭代器可能会消耗它
# 错误示例 gen = (x for x in range(5)) print(list(gen)) # 打印并消耗迭代器 for x in gen: # 这个循环不会执行,因为gen已被消耗 print(x) # 正确做法 gen = (x for x in range(5)) # 如果需要查看内容又不想消耗迭代器,创建一个副本 gen, debug_gen = itertools.tee(gen) print(list(debug_gen)) # 使用副本进行调试 for x in gen: # 原始生成器仍然可用 print(x) -
混淆迭代器和可迭代对象:理解它们的区别很重要
# 可迭代对象可以多次迭代 iterable = [1, 2, 3] for _ in range(2): for item in iterable: print(item, end=' ') print() # 迭代器只能被迭代一次 iterator = iter([1, 2, 3]) for item in iterator: print(item, end=' ') # 1 2 3 print() for item in iterator: print(item, end=' ') # 不会打印任何内容
总结
- 可迭代对象是能够一次返回一个元素的对象,它们实现了
__iter__()方法或序列的__getitem__()方法。 - 不可迭代对象不能直接用于循环或其他期望可迭代对象的上下文中。
- 迭代器是实现了
__iter__()和__next__()方法的对象,能够跟踪迭代状态,一次返回一个值。 - 生成器是使用
yield语句创建的特殊迭代器,能够简化迭代器的创建并提高内存效率。
迭代器和生成器特别适合处理大型数据集或创建数据处理管道,它们让你能够以更少的内存消耗处理更多的数据。
634

被折叠的 条评论
为什么被折叠?



