python易混淆知识点(十五)迭代器

迭代器

Python 中不仅可以转换基本类型为迭代器,还可以直接定义自定义的迭代器。 这是 Python 非常强大的特性。

一、内置类型转换为迭代器

1. 列表 (List) → 迭代器

python 复制代码
my_list = [1, 2, 3, 4, 5]
list_iter = iter(my_list)

print(next(list_iter))  # 输出: 1
print(next(list_iter))  # 输出: 2
print(next(list_iter))  # 输出: 3

# 也可以用 for 循环
for item in list_iter:  # 从第4个元素开始
    print(item)  # 输出: 4, 5

2. 元组 (Tuple) → 迭代器

python 复制代码
my_tuple = ('apple', 'banana', 'cherry')
tuple_iter = iter(my_tuple)

print(next(tuple_iter))  # 'apple'
print(next(tuple_iter))  # 'banana'

# 转换为列表查看剩余
print(list(tuple_iter))  # ['cherry']

3. 字典 (Dict) → 迭代器

字典有多种迭代方式:

python 复制代码
my_dict = {'name': 'Alice', 'age': 25, 'city': 'Beijing'}

# 方法1:默认迭代键(keys)
dict_iter_keys = iter(my_dict)
print(next(dict_iter_keys))  # 'name'(Python 3.7+ 保持插入顺序)
print(next(dict_iter_keys))  # 'age'

# 方法2:明确迭代键
dict_iter = iter(my_dict.keys())
print(next(dict_iter))  # 'name'

# 方法3:迭代值
dict_iter_values = iter(my_dict.values())
print(next(dict_iter_values))  # 'Alice'

# 方法4:迭代键值对
dict_iter_items = iter(my_dict.items())
print(next(dict_iter_items))  # ('name', 'Alice')

4. 字符串 (String) → 迭代器

字符串也可以迭代:

python 复制代码
my_string = "Hello"
str_iter = iter(my_string)

print(next(str_iter))  # 'H'
print(next(str_iter))  # 'e'
print(''.join(str_iter))  # 'llo'

5. 集合 (Set) → 迭代器

python 复制代码
my_set = {1, 3, 5, 7, 9}
set_iter = iter(my_set)

print(next(set_iter))  # 1(集合无序,顺序不确定)
print(next(set_iter))  # 3

转换为迭代器的不同方法对比

数据类型 创建迭代器方法 迭代内容 示例
列表 iter(lst) 列表元素 [1, 2, 3]1, 2, 3
元组 iter(tup) 元组元素 ('a', 'b')'a', 'b'
字典 iter(dict) 字典的键 {'a':1}'a'
字典键 iter(dict.keys()) 字典的键 {'a':1}'a'
字典值 iter(dict.values()) 字典的值 {'a':1}1
字典项 iter(dict.items()) (键, 值)对 {'a':1}('a', 1)
字符串 iter(string) 单个字符 "hi"'h', 'i'
集合 iter(set) 集合元素 {1, 2}1, 2(顺序不定)

完整示例:各种数据类型的迭代

python 复制代码
# 定义各种数据结构
my_list = [10, 20, 30]
my_tuple = ('x', 'y', 'z')
my_dict = {'a': 1, 'b': 2, 'c': 3}
my_string = "Python"
my_set = {100, 200, 300}

def iterate_data(data, data_type):
    """通用迭代函数"""
    print(f"\n迭代 {data_type}:")
    iterator = iter(data)
    
    try:
        for i in range(5):  # 最多尝试5次
            print(f"  第{i+1}次next(): {next(iterator)}")
    except StopIteration:
        print("  迭代结束")

# 测试各种类型
iterate_data(my_list, "列表")
iterate_data(my_tuple, "元组")
iterate_data(my_dict, "字典(默认迭代键)")
iterate_data(my_dict.values(), "字典的值")
iterate_data(my_string, "字符串")
iterate_data(my_set, "集合")

实际应用:数据处理

场景1:批量处理数据

python 复制代码
# 有一个数据列表,需要分批处理
data_list = list(range(100))  # 0-99
data_iter = iter(data_list)

batch_size = 10
batch_num = 0

while True:
    batch = []
    try:
        for _ in range(batch_size):
            batch.append(next(data_iter))
    except StopIteration:
        if batch:  # 处理最后一批
            print(f"批次 {batch_num}: {batch}")
        break
    
    print(f"批次 {batch_num}: {batch}")
    batch_num += 1

场景2:链式迭代多个数据集

python 复制代码
# 合并多个数据源的迭代器
def chain_iterators(*iterables):
    """链式迭代多个可迭代对象"""
    for iterable in iterables:
        yield from iterable

# 使用
list_data = [1, 2, 3]
tuple_data = (4, 5, 6)
dict_keys = {'a': 1, 'b': 2}.keys()

combined_iter = chain_iterators(list_data, tuple_data, dict_keys)
print(list(combined_iter))  # [1, 2, 3, 4, 5, 6, 'a', 'b']

场景3:创建自定义迭代器

python 复制代码
class DataLoader:
    """模拟PyTorch DataLoader的简单版本"""
    def __init__(self, data, batch_size=2):
        self.data = data
        self.batch_size = batch_size
    
    def __iter__(self):
        self.index = 0
        return self
    
    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        
        batch = self.data[self.index:self.index + self.batch_size]
        self.index += self.batch_size
        return batch

# 使用自定义迭代器
loader = DataLoader([1, 2, 3, 4, 5, 6], batch_size=2)
for batch in loader:
    print(f"批次: {batch}")
# 输出:
# 批次: [1, 2]
# 批次: [3, 4]
# 批次: [5, 6]

重要注意事项

  1. 迭代器只能前进不能后退

    python 复制代码
    data = [1, 2, 3, 4]
    it = iter(data)
    
    print(next(it))  # 1
    print(next(it))  # 2
    # 无法回到1
  2. 迭代器会消耗数据

    python 复制代码
    it = iter([1, 2, 3])
    list(it)  # [1, 2, 3]
    list(it)  # [],已经耗尽
  3. 可迭代对象 vs 迭代器

    python 复制代码
    lst = [1, 2, 3]  # 可迭代对象
    it = iter(lst)    # 迭代器
    
    # 可迭代对象可以多次创建迭代器
    it1 = iter(lst)
    it2 = iter(lst)
  4. 判断是否为迭代器

    python 复制代码
    from collections.abc import Iterator
    
    lst = [1, 2, 3]
    it = iter(lst)
    
    print(isinstance(lst, Iterator))  # False
    print(isinstance(it, Iterator))   # True

二、直接定义迭代器的3种方法

方法1:使用 生成器函数(最常用)

python 复制代码
def my_generator(start, end):
    """生成从 start 到 end 的整数"""
    current = start
    while current <= end:
        yield current  # yield 是关键!
        current += 1

# 使用
gen = my_generator(1, 5)  # 这已经是一个迭代器
print(type(gen))  # <class 'generator'>

print(next(gen))  # 1
print(next(gen))  # 2
print(list(gen))  # [3, 4, 5](剩余的部分)

方法2:使用 生成器表达式(简洁版)

python 复制代码
# 类似列表推导式,但用圆括号
gen_expr = (x**2 for x in range(5))  # 生成器表达式
print(type(gen_expr))  # <class 'generator'>

print(next(gen_expr))  # 0
print(next(gen_expr))  # 1
print(list(gen_expr))  # [4, 9, 16]

方法3:创建 迭代器类(最灵活)

python 复制代码
class MyIterator:
    """自定义迭代器类"""
    def __init__(self, start, end):
        self.current = start
        self.end = end
    
    def __iter__(self):
        """返回迭代器自身"""
        return self
    
    def __next__(self):
        """返回下一个值"""
        if self.current > self.end:
            raise StopIteration
        else:
            value = self.current
            self.current += 1
            return value

# 使用
my_iter = MyIterator(1, 3)
print(type(my_iter))  # <class '__main__.MyIterator'>

print(next(my_iter))  # 1
print(next(my_iter))  # 2
print(next(my_iter))  # 3
print(next(my_iter))  # 抛出 StopIteration

三、迭代器的内存优势

对比:列表 vs 生成器

python 复制代码
import sys

# 方法1:使用列表(占用大量内存)
def get_numbers_list(n):
    """返回包含前n个数字的列表"""
    numbers = []
    for i in range(n):
        numbers.append(i)
    return numbers

# 方法2:使用生成器(节省内存)
def get_numbers_generator(n):
    """生成前n个数字"""
    for i in range(n):
        yield i

# 对比内存使用
n = 1000000

# 列表:一次性在内存中创建所有元素
list_numbers = get_numbers_list(n)
print(f"列表占用内存: {sys.getsizeof(list_numbers)} 字节")  # 约 8MB

# 生成器:一次只生成一个元素
gen_numbers = get_numbers_generator(n)
print(f"生成器占用内存: {sys.getsizeof(gen_numbers)} 字节")  # 约 100 字节

# 但只能使用一次
print(sum(gen_numbers))  # 正常工作
print(sum(gen_numbers))  # 0(生成器已耗尽)

四、高级迭代器技巧

1. itertools 模块(标准库中的迭代器工具)

python 复制代码
import itertools

# 无限迭代器
counter = itertools.count(start=10, step=2)
print(next(counter))  # 10
print(next(counter))  # 12
print(next(counter))  # 14

# 循环迭代器
cycle_iter = itertools.cycle(['A', 'B', 'C'])
print(next(cycle_iter))  # A
print(next(cycle_iter))  # B
print(next(cycle_iter))  # C
print(next(cycle_iter))  # A(又回到开头)

# 排列组合
permutations = itertools.permutations([1, 2, 3], 2)
print(list(permutations))  # [(1,2), (1,3), (2,1), (2,3), (3,1), (3,2)]

2. yield from 语法

python 复制代码
def chain_generators(*iterables):
    """连接多个生成器"""
    for it in iterables:
        yield from it  # 委托给子生成器

# 使用
result = chain_generators([1, 2, 3], (4, 5, 6), {7, 8, 9})
print(list(result))  # [1, 2, 3, 4, 5, 6, 8, 9, 7](集合顺序不确定)

五、如何选择合适的迭代器定义方式

场景 推荐方式 示例
简单序列生成 生成器表达式 (x**2 for x in range(10))
复杂数据生成 生成器函数 def read_file(): yield line
需要状态维护 迭代器类 class DataLoader:
需要重复杂用 可迭代对象 实现 __iter__() 返回新迭代器
组合现有迭代器 itertools 模块 itertools.chain()

六、总结

  • Python 可以直接定义迭代器,而不仅限于转换基本类型

  • 三种主要方法

    • 生成器函数(yield 关键字)
    • 生成器表达式(圆括号推导式)
    • 迭代器类(实现 __iter____next__
  • 在深度学习中,自定义迭代器非常有用,可以:

    • 实现自定义 DataLoader
    • 处理流式数据
    • 节省内存(特别是处理大数据时)
    • 实现复杂的数据预处理流
  • 所有可迭代对象 都可以用 iter() 转换为迭代器

  • 列表、元组:迭代元素

  • 字典 :默认迭代键,也可用 .values().items() 迭代值和键值对

  • 字符串:迭代字符

  • 集合:迭代元素(顺序不确定)

  • 迭代器是单向的、消耗性的 ,使用 next() 逐个获取元素

相关推荐
t198751282 小时前
基于MATLAB的Bezier曲线曲面绘制实现
开发语言·matlab
molaifeng2 小时前
从 utf8.RuneCountInString 看 Go 是如何高性能、安全地解码 UTF-8 的
开发语言·安全·golang
小此方2 小时前
Re: ゼロから学ぶ C++ 入門(七)类和对象·第四篇:拷贝构造函数&赋值运算符重载
开发语言·c++
棒棒的皮皮2 小时前
【OpenCV】Python图像处理之开/闭运算
图像处理·python·opencv·计算机视觉
月明长歌2 小时前
【码道初阶】【LeetCode387】如何高效找到字符串中第一个不重复的字符?
java·开发语言·数据结构·算法·leetcode·哈希算法
张哈大2 小时前
免费薅国产旗舰 LLM!GLM-4.7+MiniMax-M2.1
人工智能·python
凯子坚持 c2 小时前
Protobuf 序列化协议深度技术白皮书与 C++ 开发全流程指南
开发语言·c++
zhlx28352 小时前
免费开源跨平台听歌自由!自定义音乐源 + 桌面歌词!LX Music 落雪音乐开源版
android·windows·macos
superman超哥2 小时前
仓颉Union类型的定义与应用深度解析
开发语言·后端·python·c#·仓颉