一、可迭代对象(Iterable)
可迭代对象是指实现了 __iter__() 方法的对象(或者实现了 __getitem__() 方法且能从 0 开始索引)。简单来说,任何可以用于 for 循环的对象都是可迭代对象。
# 常见的可迭代对象
numbers = [1, 2, 3] # 列表
text = "hello"# 字符串
d = {"a": 1, "b": 2} # 字典
s = {1, 2, 3} # 集合
for item in numbers:
print(item)
1.1 判断是否可迭代
可以使用 isinstance() 和 collections.abc.Iterable 来判断。
from collections.abc import Iterable
print(isinstance([1, 2, 3], Iterable)) # True
print(isinstance(123, Iterable)) # False
1.2 可迭代对象的本质
可迭代对象实现了 __iter__() 方法,该方法返回一个迭代器。
classMyIterable:
def__init__(self, data):
self.data = data
def__iter__(self):
# 返回一个迭代器对象
return MyIterator(self.data)
# 但通常我们会直接让类同时实现 __iter__ 和 __next__
二、迭代器(Iterator)
迭代器是实现了 __iter__() 和 __next__() 方法的对象。它代表一个数据流,可以通过 next() 函数逐个获取元素,当没有元素时抛出 StopIteration 异常。
numbers = [1, 2, 3]
it = iter(numbers) # 获取迭代器
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
print(next(it)) # StopIteration
2.1 迭代器的特点
- • 只能向前:迭代器只能逐个向后获取元素,不能回退。
- • 一次性:迭代器遍历完后,不能再使用(除非重新创建)。
- • 惰性计算:迭代器不会一次性加载所有元素,而是按需生成。
2.2 自定义迭代器
classCountDown:
"""倒计时迭代器"""
def__init__(self, start):
self.current = start
def__iter__(self):
returnself
def__next__(self):
ifself.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value
cd = CountDown(5)
for num in cd:
print(num) # 5, 4, 3, 2, 1
三、可迭代对象 vs 迭代器
| | |
| __iter__() | __iter__() |
| | |
| | iter(list) |
| for | for |
# 可迭代对象可以多次遍历
lst = [1, 2, 3]
for x in lst:
print(x)
for x in lst: # 第二次遍历
print(x)
# 迭代器只能遍历一次
it = iter(lst)
for x in it:
print(x)
for x in it: # 第二次遍历没有输出
print(x)
四、生成器(Generator)
生成器是一种特殊的迭代器,使用 yield 关键字定义。它让函数能够“记住”执行状态,每次 next() 调用时从上次暂停的地方继续执行。
4.1 生成器函数
使用 yield 而不是 return 的函数就是生成器函数,返回一个生成器对象。
defcount_down(n):
while n > 0:
yield n
n -= 1
gen = count_down(5)
print(next(gen)) # 5
print(next(gen)) # 4
print(list(gen)) # [3, 2, 1]
4.2 生成器的执行流程
生成器函数的执行是“惰性”的,每次 next() 调用时才执行到下一个 yield。
deftest_generator():
print("开始执行")
yield1
print("执行到 yield 2 之前")
yield2
print("执行结束")
gen = test_generator()
print(next(gen)) # 开始执行 \n 1
print(next(gen)) # 执行到 yield 2 之前 \n 2
print(next(gen)) # 执行结束 \n StopIteration
4.3 生成器表达式
生成器表达式类似于列表推导式,但使用圆括号而不是方括号。它是惰性计算的。
# 列表推导式:立即生成所有元素
lst = [x**2for x inrange(10)] # 占用内存
# 生成器表达式:惰性生成
gen = (x**2for x inrange(10)) # 不占用内存
print(next(gen)) # 0
print(next(gen)) # 1
五、深入理解 for 循环的工作原理
for 循环本质上是对迭代器协议的封装:
# for item in iterable:
# print(item)
# 等价于:
iterator = iter(iterable)
whileTrue:
try:
item = next(iterator)
print(item)
except StopIteration:
break
六、实战案例
6.1 斐波那契数列生成器
deffibonacci():
"""无限生成斐波那契数列"""
a, b = 0, 1
whileTrue:
yield a
a, b = b, a + b
fib = fibonacci()
for _ inrange(10):
print(next(fib)) # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
6.2 按块读取大文件
defread_in_chunks(file_path, chunk_size=1024):
"""生成器,按块读取大文件"""
withopen(file_path, 'rb') as f:
whileTrue:
chunk = f.read(chunk_size)
ifnot chunk:
break
yield chunk
for chunk in read_in_chunks('large_file.bin'):
process(chunk) # 逐块处理,节省内存
6.3 实现自己的 range 函数
defmy_range(start, stop=None, step=1):
if stop isNone:
start, stop = 0, start
if step == 0:
raise ValueError("step 不能为 0")
while (step > 0and start < stop) or (step < 0and start > stop):
yield start
start += step
for i in my_range(1, 10, 2):
print(i, end=' ') # 1 3 5 7 9
6.4 链式生成器(管道)
defread_lines(file_path):
withopen(file_path, 'r') as f:
for line in f:
yield line.strip()
deffilter_empty(lines):
for line in lines:
if line:
yield line
defto_upper(lines):
for line in lines:
yield line.upper()
# 组合生成器
lines = read_lines('data.txt')
non_empty = filter_empty(lines)
upper_lines = to_upper(non_empty)
for line in upper_lines:
print(line)
七、生成器的 send() 和 close() 方法
7.1 send():向生成器发送值
defaccumulator():
total = 0
whileTrue:
value = yield total # yield 可以接收外部传入的值
if value isNone:
break
total += value
acc = accumulator()
next(acc) # 启动生成器
print(acc.send(10)) # 10
print(acc.send(20)) # 30
print(acc.send(30)) # 60
# acc.close() # 关闭生成器
7.2 throw():向生成器抛出异常
defgenerator():
try:
yield1
yield2
except ValueError:
yield"处理了异常"
gen = generator()
print(next(gen)) # 1
print(gen.throw(ValueError)) # 处理了异常
八、总结
| | |
| 可迭代对象 | | |
| 迭代器 | | 实现 __iter__() 和 __next__() |
| 生成器 | | |
核心要点:
- • 生成器表达式比列表推导式更节省内存,适合处理大数据。
掌握迭代器和生成器,可以让你写出更高效、更 Pythonic 的代码,特别是在处理大数据流或需要惰性计算时,它们是不可或缺的工具。