目录

Python 迭代器与可迭代对象

学习目标

  • 理解可迭代对象(Iterable)和迭代器(Iterator)的概念
  • 掌握 iter()next() 函数的使用
  • 学会自定义迭代器
  • 理解迭代器的工作原理和优势

1. 可迭代对象(Iterable)

可迭代对象是指可以使用 for 循环遍历的对象。

1.1 常见的可迭代对象

# 列表
my_list = [1, 2, 3]
for item in my_list:
    print(item)

# 字符串
my_str = "Hello"
for char in my_str:
    print(char)

# 字典(迭代的是键)
my_dict = {"a": 1, "b": 2}
for key in my_dict:
    print(key)

# 元组
my_tuple = (10, 20, 30)
for num in my_tuple:
    print(num)

# 集合
my_set = {1, 2, 3}
for item in my_set:
    print(item)

1.2 如何判断是否为可迭代对象

使用 collections.abc 模块中的 Iterable 进行判断:

from collections.abc import Iterable

# 判断对象是否可迭代
print(isinstance([1, 2, 3], Iterable))  # True
print(isinstance("hello", Iterable))      # True
print(isinstance({"a": 1}, Iterable))     # True
print(isinstance(100, Iterable))          # False
print(isinstance(3.14, Iterable))         # False

2. 迭代器(Iterator)

迭代器是一个可以记住遍历位置的对象,它从集合的第一个元素开始访问,直到所有元素被访问完结束。

2.1 迭代器的两个核心方法

# iter() - 获取迭代器
# next() - 获取下一个元素

my_list = [1, 2, 3]
iterator = iter(my_list)

print(next(iterator))  # 1
print(next(iterator))  # 2
print(next(iterator))  # 3
# print(next(iterator))  # StopIteration 异常!

2.2 迭代器的工作原理

def manual_iteration():
    """手动模拟 for 循环的迭代过程"""
    my_list = ["a", "b", "c"]
    iterator = iter(my_list)
    
    while True:
        try:
            item = next(iterator)
            print(f"获取到: {item}")
        except StopIteration:
            print("迭代结束")
            break

manual_iteration()

输出:

获取到: a
获取到: b
获取到: c
迭代结束

2.3 如何判断是否为迭代器

from collections.abc import Iterator, Iterable

my_list = [1, 2, 3]

# 列表是可迭代对象,但不是迭代器
print(isinstance(my_list, Iterable))   # True
print(isinstance(my_list, Iterator))   # False

# 通过 iter() 获取迭代器
iterator = iter(my_list)
print(isinstance(iterator, Iterator))  # True
print(isinstance(iterator, Iterable))  # True(迭代器也是可迭代对象)

3. 自定义迭代器

通过实现 __iter__()__next__() 方法来自定义迭代器。

3.1 自定义范围迭代器

class MyRange:
    """自定义范围迭代器"""
    
    def __init__(self, start, end, step=1):
        self.start = start
        self.end = end
        self.step = step
        self.current = start
    
    def __iter__(self):
        """返回迭代器对象自身"""
        self.current = self.start  # 重置位置
        return self
    
    def __next__(self):
        """返回下一个元素"""
        if self.current >= self.end:
            raise StopIteration
        
        value = self.current
        self.current += self.step
        return value

# 使用自定义迭代器
for num in MyRange(0, 5):
    print(num, end=" ")  # 0 1 2 3 4

print()

# 支持步长
for num in MyRange(0, 10, 2):
    print(num, end=" ")  # 0 2 4 6 8

3.2 自定义列表反向迭代器

class ReverseIterator:
    """反向迭代器"""
    
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    
    def __iter__(self):
        self.index = len(self.data)
        return self
    
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.data[self.index]

# 反向迭代
my_list = ["apple", "banana", "cherry"]
for item in ReverseIterator(my_list):
    print(item)

输出:

cherry
banana
apple

3.3 自定义斐波那契数列迭代器

class FibonacciIterator:
    """斐波那契数列迭代器"""
    
    def __init__(self, max_count):
        self.max_count = max_count
        self.count = 0
        self.a, self.b = 0, 1
    
    def __iter__(self):
        self.count = 0
        self.a, self.b = 0, 1
        return self
    
    def __next__(self):
        if self.count >= self.max_count:
            raise StopIteration
        
        value = self.a
        self.a, self.b = self.b, self.a + self.b
        self.count += 1
        return value

# 生成前10个斐波那契数
fib = FibonacciIterator(10)
print("斐波那契数列:", list(fib))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

4. 迭代器的优势

4.1 惰性计算(Lazy Evaluation)

# 列表会一次性占用大量内存
big_list = list(range(1000000))

# 迭代器按需生成,节省内存
big_iterator = iter(range(1000000))

# 每次只取一个值
print(next(big_iterator))  # 0
print(next(big_iterator))  # 1

4.2 无限序列

class InfiniteCounter:
    """无限计数器"""
    
    def __init__(self, start=0):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        value = self.current
        self.current += 1
        return value

# 取前5个
from itertools import islice
counter = InfiniteCounter()
first_five = list(islice(counter, 5))
print(first_five)  # [0, 1, 2, 3, 4]

4.3 只能遍历一次

iterator = iter([1, 2, 3])

# 第一次遍历
for item in iterator:
    print(item)  # 1 2 3

# 第二次遍历 - 迭代器已耗尽
for item in iterator:
    print(item)  # 无输出

5. 可迭代对象 vs 迭代器

特性 可迭代对象 (Iterable) 迭代器 (Iterator)
__iter__()
__next__()
可重复使用 否(一次性)
示例 list, str, dict iter(list) 的结果
# 可迭代对象每次调用 iter() 都返回新迭代器
my_list = [1, 2, 3]

iter1 = iter(my_list)
iter2 = iter(my_list)

print(iter1 is iter2)  # False(不同对象)

# 迭代器调用 iter() 返回自身
iterator = iter(my_list)
print(iter(iterator) is iterator)  # True(同一个对象)

6. 内置迭代工具

6.1 enumerate

fruits = ["apple", "banana", "cherry"]

# 同时获取索引和值
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")

# 指定起始索引
for index, fruit in enumerate(fruits, start=1):
    print(f"{index}: {fruit}")

6.2 zip

names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
cities = ["北京", "上海", "广州"]

# 并行迭代多个序列
for name, age, city in zip(names, ages, cities):
    print(f"{name}, {age}岁, {city}")

# 使用 zip 创建字典
info = dict(zip(names, ages))
print(info)  # {'Alice': 25, 'Bob': 30, 'Charlie': 35}

6.3 itertools 模块

import itertools

# count - 无限计数
for i in itertools.count(10, 2):
    if i > 20:
        break
    print(i, end=" ")  # 10 12 14 16 18 20
print()

# cycle - 无限循环
for i, char in enumerate(itertools.cycle("AB")):
    if i >= 6:
        break
    print(char, end=" ")  # A B A B A B
print()

# chain - 连接多个迭代器
for item in itertools.chain([1, 2], [3, 4], [5, 6]):
    print(item, end=" ")  # 1 2 3 4 5 6
print()

# combinations - 组合
for combo in itertools.combinations([1, 2, 3], 2):
    print(combo, end=" ")  # (1, 2) (1, 3) (2, 3)
print()

# permutations - 排列
for perm in itertools.permutations([1, 2, 3], 2):
    print(perm, end=" ")  # (1, 2) (1, 3) (2, 1) (2, 3) (3, 1) (3, 2)

本节小结

  • 可迭代对象:能被 for 循环遍历的对象,实现了 __iter__() 方法
  • 迭代器:实现了 __iter__()__next__() 的对象,只能遍历一次
  • 自定义迭代器:通过实现这两个魔术方法创建自己的迭代器
  • 优势:惰性计算节省内存、支持无限序列
  • 工具enumeratezipitertools 等内置工具非常实用

练习

  1. 创建一个自定义迭代器 Countdown,实现倒计时功能(从 n 到 0)
  2. 实现一个迭代器,可以遍历文件的每一行并返回行号和内容
  3. 使用 itertools 生成扑克牌的所有组合
  4. 写一个迭代器,能够遍历二叉树的节点(先序遍历)