Python 迭代器与生成器

作者:copyer_xyf日期:2026/6/7

本文面向已有前端开发基础、正在学习 Python 的开发者。

迭代器和生成器解决的是同一个问题:数据不一定要一次性全部准备好,可以在需要的时候一个一个取出来。前端里最接近的经验是 for...ofSymbol.iterator、生成器函数 function*yield

这几个概念可以先合在一起记:

  • 可迭代对象表示“可以被遍历的数据源”
  • 迭代器表示“真正负责一步一步取值的对象”
  • 生成器表示“用 yield 快速创建出来的迭代器”
    后面的 for 循环,本质上就是先从可迭代对象拿到迭代器,再不断从迭代器里取下一个值。

一、先把概念边界讲清楚

先记住一条主线:

1for item in obj
2  -> 先调用 iter(obj) 拿到迭代器
3  -> 再不断调用 next(迭代器)
4  -> 遇到 StopIteration 后结束
5

所以这几个概念可以这样分:

概念关注点PythonJavaScript
可迭代对象能不能开始遍历能被 iter() 接受有 Symbol.iterator
迭代器这次遍历走到哪里了能被 next() 调用有 next()
迭代协议遍历接口怎么约定iter() -> next() -> 结束时抛异常Symbol.iterator -> next()
生成器怎么快速创建迭代器函数体里写 yieldfunction* + yield

两门语言只是接口名字和结束方式不同:

1Python:
2可迭代对象 -- iter() --> 迭代器 -- next() --> value / StopIteration
3
4JS:
5可迭代对象 -- Symbol.iterator() --> 迭代器 -- next() --> { value, done }
6

最关键的边界是:可迭代对象表示“能开始一次遍历”,迭代器表示“这次遍历本身”。列表、字符串这类可迭代对象可以反复遍历,因为每次都能创建新的迭代器;已经创建出来的迭代器通常只能向前走,取过的值不会自动回到起点。

生成器不算新的遍历体系,它只是更省事的迭代器写法。手写迭代器要自己维护位置和结束条件;生成器用 yield 保存暂停点,每次 next() 都从上一次暂停的位置继续执行。

是不是有点懵😳

二、从 for 循环看迭代过程

JavaScript 里,一个对象只要实现了 Symbol.iterator,就可以被 for...of 消费。

1const names = ["张三", "李四", "王五"];
2
3for (const name of names) {
4  console.log(name);
5}
6

如果拆开看,for...of 背后大概做了这些事:

1const iterator = names[Symbol.iterator]();
2
3console.log(iterator.next()); // { value: '张三', done: false }
4console.log(iterator.next()); // { value: '李四', done: false }
5console.log(iterator.next()); // { value: '王五', done: false }
6console.log(iterator.next()); // { value: undefined, done: true }
7

所以前端里有两层概念:

概念判断方式作用
iterable有 Symbol.iterator可以交给 for...of
iterator有 next()可以一步一步取值

Python 也有这两层,只是名字和结束方式不同:

JavaScriptPython
obj[Symbol.iterator]()iter(obj)
iterator.next()next(iterator)
返回 { value, done }返回本次值
done: true 表示结束抛出 StopIteration 表示结束

把这个对照关系记住,后面的 Python 语法就会清楚很多。

三、可迭代对象 iterable

可迭代对象就是:能被 for 循环遍历的对象。

1names = ['张三', '李四', '王五']
2cities = ('北京', '上海', '深圳')
3msg = 'hello'
4
5for name in names:
6    print(name)
7

这些对象都能被 for 遍历,所以它们都是可迭代对象。

从协议角度看,可迭代对象要能被 iter() 接受:

1names = ['张三', '李四', '王五']
2msg = 'hello'
3age = 18
4
5print(iter(names)) # list_iterator
6print(iter(msg))   # str_iterator
7
8# print(iter(age)) # TypeError: 'int' object is not iterable
9

也可以用 hasattr 粗略观察:

1names = ['张三', '李四', '王五']
2msg = 'hello'
3age = 18
4
5print(hasattr(names, '__iter__')) # True
6print(hasattr(msg, '__iter__'))   # True
7print(hasattr(age, '__iter__'))   # False
8

这里的 __iter__ 是 Python 的魔法方法。平时开发一般不直接写 names.__iter__(),而是用内置函数 iter(names)

1obj.__iter__()
2  -> 底层魔法方法
3
4iter(obj)
5  -> 日常使用方式
6  -> 内部会调用 obj.__iter__()
7

四、迭代器 iterator

调用 iter(可迭代对象) 之后,会得到一个迭代器。

1names = ['张三', '李四', '王五']
2
3it = iter(names)
4
5print(next(it)) # 张三
6print(next(it)) # 李四
7print(next(it)) # 王五
8print(next(it)) # StopIteration
9

迭代器的核心能力是:记住当前取到哪里了,每次 next() 返回下一个值。

也就是说,迭代器内部有状态,类似一个指针:

1初始位置
2  -> next() 取第 1 
3  -> next() 取第 2 
4  -> next() 取第 3 
5  -> 没有数据了,抛 StopIteration
6

如果用 while 手动模拟 for,大概是这样:

1names = ['张三', '李四', '王五']
2
3it = iter(names)
4
5while True:
6    try:
7        item = next(it)
8        print(item)
9    except StopIteration:
10        break
11

所以 for item in names 并不神秘,它背后就是:

1先调用 iter(names) 得到迭代器
2再不断调用 next(迭代器)
3遇到 StopIteration 后结束循环
4

迭代器自己也是可迭代对象

迭代器一般也有 __iter__ 方法,并且返回自己。

1names = ['张三', '李四', '王五']
2
3it = iter(names)
4
5print(iter(it) is it) # True
6

这样设计的原因是:for 循环第一步一定会调用 iter(x)。如果传进去的已经是迭代器,iter(迭代器) 必须也能正常工作。

迭代器会被消耗

迭代器不是列表,它是一次性向前取值的过程。

1names = ['张三', '李四', '王五']
2
3it = iter(names)
4
5print(next(it)) # 张三
6
7for name in it:
8    print(name)
9
10# 只会继续输出:
11# 李四
12# 王五
13

前面已经被 next(it) 取走的值,不会在后面的 for 里重新出现。

这点很像前端里已经调用过几次 iterator.next() 后,再继续 for...of 或继续 .next(),状态会接着往后走,而不是自动重置。

五、自定义可迭代对象

如果希望自己的类能被 for 遍历,就要实现迭代器协议。

需求:让 Person 实例可以被遍历,依次取出姓名、年龄、性别、地址。

1p1 = Person('张三', 18, '男', '北京昌平')
2
3for item in p1:
4    print(item)
5

写法一:对象和迭代器分开

这种写法最清晰:Person 负责保存业务数据,PersonIterator 负责遍历过程。

1class Person:
2    def __init__(self, name, age, gender, address):
3        self.name = name
4        self.age = age
5        self.gender = gender
6        self.address = address
7
8    def __iter__(self):
9        # 返回一个专门负责遍历 Person 的迭代器
10        return PersonIterator(self)
11
12
13class PersonIterator:
14    def __init__(self, person):
15        # 保存外部传进来的 Person 对象
16        self.person = person
17        # 记录当前取到哪个位置
18        self.index = 0
19        # 配置要遍历哪些字段
20        self.attrs = [
21            person.name,
22            person.age,
23            person.gender,
24            person.address,
25        ]
26
27    def __iter__(self):
28        # 迭代器的 __iter__ 返回自己
29        return self
30
31    def __next__(self):
32        if self.index >= len(self.attrs):
33            raise StopIteration
34
35        value = self.attrs[self.index]
36        self.index += 1
37        return value
38

执行:

1p1 = Person('张三', 18, '男', '北京昌平')
2
3for item in p1:
4    print(item)
5

输出:

1张三
218
3
4北京昌平
5

这个写法适合业务对象比较复杂的场景。业务对象和遍历状态分开,Person 不需要关心当前遍历到第几个字段。

写法二:对象自己也是迭代器

也可以让 Person 同时实现 __iter____next__

1class Person:
2    def __init__(self, name, age, gender, address):
3        self.name = name
4        self.age = age
5        self.gender = gender
6        self.address = address
7        self.attrs = [name, age, gender, address]
8
9    def __iter__(self):
10        self.index = 0
11        return self
12
13    def __next__(self):
14        if self.index >= len(self.attrs):
15            raise StopIteration
16
17        value = self.attrs[self.index]
18        self.index += 1
19        return value
20

这种写法代码更少,但要注意:遍历状态放在对象自己身上。多个地方同时遍历同一个对象时,更容易相互影响。

学习阶段可以先写这种,真实业务里更推荐“对象和迭代器分开”,职责更清楚。

六、为什么需要迭代器

迭代器最大的价值是惰性计算:不一次性生成所有结果,而是在需要时才计算下一个。

比如生成斐波那契数列,如果一次性生成 100000 个数字并放进列表,内存会越来越大。

1def fib_list(total):
2    result = []
3    a = 0
4    b = 1
5
6    for _ in range(total):
7        result.append(a)
8        a, b = b, a + b
9
10    return result
11

如果改成迭代器,每次只返回当前这个数:

1class Fibo:
2    def __init__(self, total):
3        self.total = total
4        self.index = 0
5        self.a = 0
6        self.b = 1
7
8    def __iter__(self):
9        return self
10
11    def __next__(self):
12        if self.index >= self.total:
13            raise StopIteration
14
15        value = self.a
16        self.a, self.b = self.b, self.a + self.b
17        self.index += 1
18        return value
19

使用:

1for number in Fibo(10):
2    print(number)
3

迭代器适合这些场景:

  • 数据量很大,不想一次性放进内存
  • 不确定用户最终会消费多少结果
  • 数据来自文件、网络、数据库游标这类流式来源
  • 每个结果只依赖当前状态和上一个状态

七、生成器 generator

生成器可以理解成:Python 帮你自动实现迭代器协议的语法糖。

只要一个函数体里出现 yield,这个函数就不是普通函数,而是生成器函数。

1def demo():
2    print('demo 函数开始执行了')
3    print(100)
4    yield '我是第 1  yield 返回的数据'
5
6    a = 200
7    print(a)
8    yield '我是第 2  yield 返回的数据'
9
10    b = 300
11    print(b)
12    return '执行结束'
13

调用生成器函数时,函数体不会立刻执行,而是返回一个生成器对象。

1d = demo()
2
3print(hasattr(d, '__iter__')) # True
4print(hasattr(d, '__next__')) # True
5

生成器对象本质上是一种迭代器,所以可以用 next() 取值:

1d = demo()
2
3print(next(d))
4print(next(d))
5
6try:
7    print(next(d))
8except StopIteration as e:
9    print(e.value) # 执行结束
10

执行过程可以这样理解:

1第一次 next()
2  -> 函数从开头执行
3  -> 遇到第一个 yield 暂停
4  -> yield 后面的值作为本次 next() 的返回值
5
6第二次 next()
7  -> 从上次暂停的位置继续执行
8  -> 遇到第二个 yield 再暂停
9
10第三次 next()
11  -> 继续执行
12  -> 遇到 return
13  ->  StopIteration
14  -> return 后面的值会放到异常对象的 value 
15

生成器和普通函数最大的差异是:普通函数一次调用跑到底,生成器函数可以在 yield 处暂停,下次再接着跑。

前端里可以对照 function*

1function* demo() {
2  console.log("demo 开始执行");
3  yield "第 1 个值";
4  yield "第 2 个值";
5}
6
7const d = demo();
8
9console.log(d.next());
10console.log(d.next());
11console.log(d.next());
12

八、yield 的几个常见写法

yield 写在循环里

最常见的生成器写法,是在循环里不断 yield

1def fib(total):
2    a = 0
3    b = 1
4
5    for _ in range(total):
6        yield a
7        a, b = b, a + b
8

使用:

1for number in fib(10):
2    print(number)
3

这比手写 class Fibo 简洁很多,但效果类似:每次需要下一个值时,才继续往后计算。

yield from

yield from 可以把另一个可迭代对象里的值依次产出。

1def demo():
2    nums = [10, 20, 30, 40]
3    yield from nums
4

它大致等价于:

1def demo():
2    nums = [10, 20, 30, 40]
3
4    for num in nums:
5        yield num
6

所以 yield from 可以记成:

1把某个可迭代对象里的数据,一个一个 yield 出去
2

send()

生成器除了能往外吐值,也能在继续执行时接收外部传进来的值。

1def demo():
2    print('demo 函数开始执行了')
3
4    a = yield '第 1  yield 的返回值'
5    print(f'a 接收到:{a}')
6
7    b = yield '第 2  yield 的返回值'
8    print(f'b 接收到:{b}')
9

使用:

1d = demo()
2
3print(next(d))        # 先启动生成器,停在第一个 yield
4print(d.send('张三')) #  '张三' 传给变量 a,然后继续执行
5
6try:
7    d.send('李四')     #  '李四' 传给变量 b,然后继续执行到函数结束
8except StopIteration:
9    print('生成器执行结束')
10

注意:第一次启动生成器时不能直接传普通值,因为代码还没有运行到任何一个 yield 位置,没有地方接收这个值。

1d = demo()
2
3# d.send('张三') # TypeError
4d.send(None)    # 等价于 next(d)
5

next() 只能取值;send(value) 既能让生成器继续执行,也能把值传回上一次暂停的 yield 表达式。

九、生成器表达式

生成器表达式是一种快速创建生成器对象的写法,长得很像列表推导式。

1nums = [10, 20, 30, 40]
2
3result1 = [n * 2 for n in nums]
4result2 = (n * 2 for n in nums)
5
6print(result1) # [20, 40, 60, 80]
7print(result2) # <generator object ...>
8

区别在于:

写法结果是否立刻生成全部结果
[n * 2 for n in nums]列表
(n * 2 for n in nums)生成器对象

生成器表达式适合“每个结果只依赖当前元素”的场景。

1nums = [10, 20, 30, 40]
2
3result = (n * 2 for n in nums)
4
5for item in result:
6    print(item)
7

它不会一次性创建 [20, 40, 60, 80],而是每次循环时才计算当前这个 item

如果数据量很小,并且后面要反复使用结果,列表推导式更直观。如果数据量很大,只需要顺序消费一遍,生成器表达式更省内存。

十、最后怎么选

可以按这个顺序判断:

1只是遍历已有 list / tuple / dict / str
2  -> 直接 for
3
4想让自己的类能被 for
5  -> 实现 __iter__
6  -> 如果要自己控制取值过程,再实现 __next__
7
8要一个一个惰性产出结果
9  -> 优先写生成器函数 yield
10
11只是把一个可迭代对象映射成另一个惰性结果
12  -> 用生成器表达式
13
14需要复杂状态、多个方法、可维护的对象封装
15  -> 手写迭代器类
16

最容易混淆的点:

问题结论
能 for 的一定是迭代器吗不一定,可能只是可迭代对象
迭代器能 for 吗能,因为迭代器的 __iter__ 返回自己
iter(obj) 做了什么调用 obj.__iter__(),拿到迭代器
next(it) 做了什么调用 it.__next__(),拿下一个值
取完后怎么结束Python 抛 StopIteration
生成器是什么用 yield 自动创建出来的迭代器
生成器会立刻执行函数体吗不会,第一次 next() 才开始执行
迭代器能重复遍历吗通常不能,它会被消耗

Python 迭代器与生成器》 是转载文章,点击查看原文


相关推荐


【架构实战】ElasticSearch搜索集群:全文检索的艺术
heimeiyingwang2026/5/31

【架构实战】ElasticSearchæœç´¢é›†ç¾¤ï¼šå ¨æ–‡æ£€ç´¢çš„è‰ºæœ¯ 倒排索引、分片副本、搜索优化、实战案例 ä¸€ã€ä»Žä¸€ä¸ªçœŸå®žçš„æ• äº‹è¯´èµ· 2024年双十一,某电商平台搜索系统在流量洪峰到来的那一刻,突


豆包收费了:3.45亿用户,一个“豆包型人格“的道歉经济学
倔强的石头_2026/5/9

5月4号,两个微博热搜几乎同时炸了——#豆包错误率# 和 #豆包笨还收费#。 前一天,豆包刚刚在App Store页面更新了付费订阅声明:标准版68元/月,加强版200元/月,专业版500元/月。作为目前国内月活超过3.45亿的AI助手——这个数字意味着大约每四个中国人里就有一个人在用豆包——这是字节跳动第一次正式给豆包贴上价格标签。 但市场的反应不是期待,而是愤怒。 “又笨又收费,说平时用免费版,经常答非所问,信息出错,逻辑也不严谨,有时候还一本正经地胡说八道,基础功能都没做好。” “免费的


解锁AI编程密码:程序员常用的10个AI提示词
小码哥_常2026/4/30

解锁AI编程密码:程序员常用的10个AI提示词 引言:AI 时代的编程利器 在当今数字化浪潮中,编程领域正经历着前所未有的变革,AI 的加入让程序员们如虎添翼。有这样一个真实的故事,程序员小李在开发一个电商项目的订单管理模块时,遇到了性能瓶颈。原本处理大量订单数据时需要耗费很长时间,导致用户在下单和查询订单状态时响应迟缓。小李尝试了各种常规优化手段,但效果甚微,他陷入了困境,项目进度也因此受阻。 后来,小李了解到可以借助 AI 来解决问题。他在 AI 编程助手的输入框中输入了这样一个提示词:“A


GPT-Image-2 真有点夯:中文不乱码了!GPT-Image-2的入口在哪?教你如何确认自己是否被灰度推送了 GPT-Image-2
摆烂工程师2026/4/21

不知道大家有没有被 OpenAI 最近推出的 GPT-Image-2 惊讶到! 这几天,我用 GPT-Image-2 制作了各种主题的图片,简直夯爆了! 首先汉字提升巨大!另外就是高密度的文字生成,几乎没有乱码! 测试出来的效果,大家直接去生成对比,目前 GPT-Image-2 就是文生图的新王。 比 Nano Banana Pro 香多了! 怎么体验 GPT-Image-2 呢? 目前,官方已经进行灰度分发到 ChatGPT 上,优先美区,只要订阅了 Plus、Pro、Business 等用户


越用越强不是广告语:拆解 Hermes Agent 的三层学习机制
小墨同学boy2026/4/12

用 AI agent 有一段时间了,有个问题一直没解决:每次开新会话,它对我的项目和习惯还是一无所知。上下文配置文件里写了不少,但写进去的是静态的——它不会自己学,也不会根据我真实的操作习惯去调整。跑得熟不熟,完全取决于我自己有没有空去维护那份文件。 Hermes Agent 是 Nous Research 今年二月发布的开源代理框架(MIT 协议),主打的就是解决这个问题——让 agent 从使用中自己学,不靠你手动补。这篇主要拆它三层学习机制怎么运转,以及和 OpenClaw 的根本差在哪里


记录 idea 启动 tomcat 控制台输出乱码问题解决
2601_949818092026/4/4

文章目录 问题现象解决排查过程 1. **检查 idea 编码设置**2. **检查 tomcat 配置**3.检查 idea 配置文件4.在 Help 菜单栏中,修改`Custom VM Options`完成后保存,并重启 idea 问题现象 运行 tomcat 后,控制台输出乱码 解决排查过程 1. 检查 idea 编码设置 进入 File -> Settings在设置窗口中,导航到 Editor -> File Encodings。确保 Gl


【iOS】Effective Objective-C第四章
库奇噜啦呼2026/3/27

【iOS】Effective Objective-C第四章 协议与分类通过委托与数据源协议进行对象间通信将类的实现代码分散到便于管理的数个分类之中勿在分类中声明属性使用“class-continuation分类“隐藏实现细节通过协议提供匿名对象 协议与分类 协议和分类都是OC的一项重要语言特性。 协议:OC不支持多重继承,因而我们把某个类该实现的一系列方法定义在协议里面。协议最为常见的用途是实现委托模式。分类:利用分类,我们无须继承子类即可直接为当前类添加方法。 通过委托与数据源协


【Linux】 Ubuntu 与 CentOS 新手安装指南,避坑要点全总结
我不是呆头2026/3/19

【Linux】Ubuntu 与 CentOS 新手安装指南,避坑要点全总结 摘要 (Abstract) 踏入 Linux 世界的第一步,往往是令人望而生畏的“安装”。在众多发行版中,Ubuntu 和 CentOS 无疑是两个最常被提及的名字:一个(Ubuntu)是桌面和开发者的宠儿,另一个(CentOS)则是企业级服务器的标杆。然而,对于新手而言,从选择版本、制作启动盘到最关键的磁盘分区,每一步都暗藏“坑点”。本文是一篇面向零基础新手的“避坑”指南,旨在通过详细对比 Ubuntu 和


人工智能、机器学习和深度学习,其实不是一回事
IvanCodes2026/3/10

一、人工智能、机器学习与深度学习的真正区别 在当今科技领域,我们经常听到人工智能、机器学习和深度学习这三个词。它们虽然相关,但含义不同。 1.1 人工智能 人工智能是计算机科学的一个分支,旨在研究如何合成与分析能够像人一样行动的计算主体。简单来说,AI 的目标是利用计算机来模拟甚至替代人类大脑的功能。 一个理想的 AI 系统通常具备以下特征:像人一样思考、 像人一样行动、理性地思考与行动。 1.2 机器学习 机器学习是实现人工智能的一种途径。它的核心定义是:赋予计算机在没有被显式


零基础搭建WordPress网站完整流程
柠檬味的Cat2026/3/2

WordPress(简称WP)作为全球占比超43%的开源内容管理系统(CMS),凭借免费易用、插件生态丰富、可扩展性强的优势,成为个人博客、企业官网、技术文档站的首选建站工具。本文针对CSDN新手用户,梳理从前期准备到网站上线、后期优化的全流程,每一步都附具体操作和避坑提示,无需专业开发基础,跟着做就能快速搭建属于自己的WP网站。 核心流程概览:准备工作(域名+服务器)→ 服务器环境配置 → WordPress程序安装 → 网站基础设置 → 主题与插件配置 → 安全加固与性能优化 → 网站上线

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读