☰
深入理解Python迭代器与生成器:for循环底层原理与实战避坑
2026/10/16 8:37:38 网站建设 项目流程

1. 先从 for 循环说起:它到底背着我们在忙什么

1.1 一个看似简单的问题:for 循环是怎么把数据一个个取出来的?

很多人写了好几年 Python,用 for 循环就跟呼吸一样自然,但要是突然被问到“for 循环底层到底怎么执行的”,可能会一下子愣住。

我记得有一次跟一个刚转 Python 的同事聊天,他说自己用for i in [1, 2, 3]感觉天经地义,直到我让他猜一下“列表里的元素是通过什么方式被访问的”,他才发现其实自己并不清楚。这并不怪他,因为语言层面把太多细节藏起来了。但恰恰是这些被藏起来的细节,决定了很多你后来会踩的坑。

先说一个最常见的误解:很多人以为 for 循环是“判断 i 在不在列表里,然后把它拿出来”。这个理解方向其实反了。Python 的 for 循环从来没有直接操作列表,它面对的其实是一个迭代器。你没看错,即使是遍历一个普通列表,for 循环真正打交道的东西,也不是列表本身。

这个问题的答案,就藏在 Python 的迭代协议里。而理解这套协议,你就等于看穿了 for 循环的真实面目。

1.2 揭秘底层:iter() 和 next() 的双人舞

要解释 for 循环背后的机制,必须先搞清楚 Python 内部设定的两个核心方法:

  • __iter__():返回一个迭代器对象,它干了什么事呢?它拿到了一个“可以不断往外吐元素”的机器。
  • __next__():迭代器向外吐一个元素;吐完了就抛出一个叫StopIteration的异常,表示“我没货了”。

for 循环的完整工作过程,其实是一套固定的三步舞:

  1. 调用iter(可迭代对象),拿到迭代器;
  2. 然后不断地调用next(迭代器),每次拿一个元素;
  3. 一旦捕获到StopIteration异常,立刻终止循环。

你可以手动模拟一下 for 循环的内部执行逻辑,看完就彻底明白了:

# 手动模拟 for 循环的内部机制 fruits = ["苹果", "香蕉", "橙子"] # 第一步:获取迭代器 it = iter(fruits) # 第二步:不断调用 next() while True: try: fruit = next(it) print(fruit) except StopIteration: # 第三步:捕获异常,退出循环 break

这两段代码的效果完全一样。你会发现一个反常识的事实:普通列表本身并不具备一次性吐元素的能力,需要通过 iter() 把它包一层,得到迭代器才能真正地遍历。列表是有__iter__方法,但真正实现__next__的那个对象,是迭代器,不是列表本身。

为什么 Python 要绕这么一圈?因为它这样设计,是为了让你能对不同数据类型用同一种遍历方式。你的对象可以是一个包含百万条记录的文件,也可以是一个无限数列,甚至是一个数据库游标——只要它们实现迭代协议,for 循环就统一能用。这种设计,就是典型的面向协议编程思想。

1.3 生活化类比:一台糖果机

如果你觉得上面概念太抽象,可以想象一台糖果机。

你投一枚硬币(调用iter()),机器会掉出一个出口装置(迭代器)。你每按一次“出糖”按钮(调用next()),机器吐出一颗糖。等糖全部吐完,你再按按钮,机器不是沉默,而是亮起红灯、发出警报——那个警报就是StopIteration异常,告诉你“真的没有了”。

这台糖果机的妙处在于:你不用关心里面的糖到底是堆在一个很大的仓库里,还是一颗一颗生产出来的。你只需要一颗一颗地拿。这也正是迭代器最大的魅力——它把生产过程和获取过程解耦了。

2. 可迭代对象和迭代器,看着像,但绝对不是一回事

2.1 两者核心区别:谁在“等糖”,谁在“做糖”

很多教程会把“可迭代对象”和“迭代器”混着说,但在实际排查 bug 的时候,这两个概念必须分得足够清楚,否则你会被一些非常诡异的现象折磨。

一句话总结:可迭代对象是那个“可以被遍历”的东西,迭代器是那个“正在被遍历”的东西。更准确地说:

  • 可迭代对象(Iterable):实现了__iter__(),可以传入iter()函数,返回一个迭代器。典型代表:列表、元组、字典、集合、字符串。
  • 迭代器(Iterator):同时实现了__iter__()和__next__(),而且它有状态。每次调用next()都会改变它内部的位置指针。

给你列个对照表,直观一点:

对比项可迭代对象迭代器
典型代表列表、元组、字符串、字典文件对象、生成器、iter(列表)的返回值
有__next__吗没有有
能被 for 循环直接遍历吗可以(内部先转成迭代器)可以
能被next()直接调吗不能能
是否消耗(一次性)不是,可以反复遍历是,遍历完就没了

实际操作中判断一个对象是不是迭代器,可以用isinstance()结合标准库里的collections.abc来做判断。这是我最常用的手法:

from collections.abc import Iterable, Iterator fruits = ["苹果", "香蕉"] print(isinstance(fruits, Iterable)) # True print(isinstance(fruits, Iterator)) # False it = iter(fruits) print(isinstance(it, Iterable)) # True print(isinstance(it, Iterator)) # True

2.2 为什么列表不是迭代器,反而会带来好处?

有人可能会问:既然迭代器这么好,为什么 Python 不直接把列表设计成迭代器?这样不就省了一步吗?

答案其实很有意思:如果列表本身就是迭代器,那么列表就不能重复遍历了。你可以试想一下,一个迭代器的特点就是“有状态”,它内部记住自己遍历到哪了。如果你对同一个列表做两次 for 循环,第二次什么都遍历不到——因为你第一次已经把“接下来该吐哪个元素”的记录消耗掉了。这显然不符合列表正常的使用习惯。

所以 Python 采用了这种“可迭代对象 + 迭代器”分离的设计:列表管数据存储,迭代器管遍历过程。每次 for 循环,都会通过iter()从列表那里拿到一个全新的迭代器,这就是为什么同一个列表可以被for循环遍历很多次,每次都能从头开始。

我第一次自己写代码时,就是把自定义的容器类设计成了“自身即迭代器”,结果第二次 for 循环遍历不上任何数据。排查了半天,最后才发现是自己在__iter__里写成了return self,而没有返回一个新的迭代器实例。

2.3 容易混淆的场景:文件对象到底是可迭代对象还是迭代器?

文件对象是很多人最容易搞混的一个典型。

其实文件对象是迭代器。也就是说,一个文件对象一旦被 for 循环遍历到底,你想再遍历一遍,是遍历不出来的。除非你用fp.seek(0)把指针拨回开头。

with open("demo.txt", "r", encoding="utf-8") as fp: for line in fp: print(line.strip()) # 第二次再想遍历,什么都拿不到了 with open("demo.txt", "r", encoding="utf-8") as fp: for line in fp: print(line.strip()) # 第一次已经读到文件末尾了,这里需要先 seek(0)

我第一次做日志分析时,就忘了这个点:读完一遍文件后,又在同一段代码里对同一个文件对象做第二次统计,结果数据全是 0。当时还以为文件编码有问题,折腾了很久,最后才发现罪魁祸首正是“文件对象是迭代器”这个属性。

3. 自己动手写迭代器:从零实现一个可迭代对象

3.1__iter__和__next__的实现要点

理论看再多,不如自己动手写一个迭代器来得深刻。其实实现一个自定义迭代器,核心只需要两个方法:

  • __iter__(self)必须返回一个迭代器。如果你只是想让这个对象可以被 for 使用,通常让它返回自身,但你得保证这个类里面有下一步的__next__方法。更规范的做法是让它返回一个全新的迭代器对象,这样每次遍历都是独立的。
  • __next__(self)负责返回下一个元素,没有元素时就抛出StopIteration。

这里有个小经验:设计迭代器时,状态变量放在哪,是整个设计的关键。如果__iter__返回自身,那这个对象本身就是迭代器,所有状态都堆在同一个地方;如果每次返回一个全新的迭代器,那多个 for 循环之间互不干扰。后者的灵活性更高,但是代码量也会多一些。

在实际项目里面,我见过太多人一股脑地实现__iter__返回 self,然后就把所有状态都挂在同一个对象上,导致嵌套 for 循环时相互干扰。我自己的习惯是:如果遍历过程不复杂,直接用生成器;如果确实要写类式迭代器,就把迭代状态单独挪出来,不要在原来的容器对象上做状态累积。

3.2 完整示例:一个倒序遍历列表的迭代器

下面我用一个自定义迭代器来演示:一个能倒序遍历数据的容器。这个示例不管在面试还是实际工程里都很典型。

class ReverseList: """把一个序列包装成可以倒序遍历的可迭代对象""" def __init__(self, data): self.data = data def __iter__(self): # 返回一个新的迭代器对象,保证多次遍历互不影响 return _ReverseIterator(self.data) class _ReverseIterator: """真正的迭代器:倒序访问数据""" def __init__(self, data): self.data = data self.index = len(data) - 1 # 注意:从最后一个元素开始 def __next__(self): if self.index < 0: raise StopIteration value = self.data[self.index] self.index -= 1 return value # 使用 rst = ReverseList([1, 2, 3, 4, 5]) for num in rst: print(num, end=" ") # 5 4 3 2 1 print() # 可以反复遍历 for num in rst: print(num, end=" ") # 5 4 3 2 1

这段代码里面有一个非常关键的设计细节:__iter__返回的是_ReverseIterator(self.data)而不是self。这样每次 for 循环都会创建一个全新的迭代器,所以上面的 for 循环连续跑两次,结果完全一致,不会因为第一次跑完导致第二次没有数据。

如果你图省事,在ReverseList里直接把self.index作为共享状态,然后在__iter__中直接return self,那就会掉进“迭代器一次性”的坑里。我第一次写类似代码的时候踩过,后来长了记性。

3.3 迭代器的状态管理:哪些状态该放哪,心里要有数

自定义迭代器的时候,最容易出问题的就是状态管理。有几种常见做法,各自适用不同场景:

  • 状态放容器对象自身:适合“单一遍历”的场景,比如日志读取、网络流读取。文件对象就是这种模式。
  • 状态放在独立的迭代器类:适合“同一个对象需要反复遍历很多次”的场景。上面ReverseList的示例就是这样。
  • 用生成器直接保存状态:如果你不需要太复杂的类结构,直接写生成器函数最省事。因为生成器内部的局部变量天然就是那个独立迭代器的状态。

很多时候,问题的本质不是“我不会写迭代器”,而是“我没有想清楚遍历这个动作到底是独立的,还是跟容器本身绑定的”。这两者在代码结构上差别非常大。我见过一个真实项目里,同事在一个资源共享类上直接写了__iter__和__next__,结果多个请求并发遍历同一个资源对象时,状态互相污染。排查了半天,最后发现问题时他说了句“我压根没想到 iter 还能这么偷懒”。确实,当你觉得“返回 self 真方便”时,要警觉一下。

4. 生成器——迭代器的亲兄弟,写起来太省事了

4.1 yield 到底是怎么工作的?

如果你觉得手写一个迭代器类太繁琐,Python 早就给你准备好了另一条路:生成器。

生成器是一种特殊的迭代器,通过函数里的yield关键字来做到“暂停 + 恢复”。每当调用next()时,函数会执行到yield那一行,把值返回,然后函数状态会被冻结;下一次再调用next(),函数会从上次暂停的位置接着往下执行,而不是从头开始。

我特别喜欢用生活里的人来类比:普通函数是一个不断向前跑、跑到头就结束的运动员;生成器是一个跑到某个标记点就会停下来打盹的旅人,你推一下他,他就往前走两步,再遇到标记点,又停下来。

下面这个生成器的例子,可以生成斐波那契数列,而且是无限序列:

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

你注意,这个函数里面其实是while True,是一个死循环。如果你用普通函数这么写早就 CPU 爆表了,但在生成器里,每调用一次next(),它只会执行到yield a就停下。所以即使循环条件永远为真,也不会出现无穷占满内存的情况——这就是惰性计算的威力,按需生产。

我当年刚学 Python 的时候,其实一直没弄懂生成器跟普通函数到底有什么本质区别,总觉得就是yield替换return而已。后来才真正理解,yield并不是简单的“换一个返回值写法”,它更像一个可恢复执行的断点。

4.2 生成器本质上就是迭代器

从协议的角度看,生成器对象天生就有__iter__和__next__,所以你可以直接把它交给next()、for循环、甚至list()来消费。

nums = (x * x for x in range(5)) print(type(nums)) # <class 'generator'> print(next(nums)) # 0 print(next(nums)) # 1 print(list(nums)) # [4, 9, 16]

看到没有,生成器表达式返回值就是一个生成器对象。而且它只能被遍历一次,遍历完就“没货了”。所以你在实际开发里千万不要觉得生成器可以反复遍历,它不能。

4.3 生成器的高级玩法:send、throw、close

大多数时候,生成器只是被 for 循环被动地调用 next()。但有些场景下,你想边迭代边给生成器传点数据,那就要用到send()了。

看这个例子:

def echo(): while True: received = yield "请说点什么" if received is None: print("你没输入内容") else: print(f"收到:{received}") e = echo() print(next(e)) # 启动生成器,输出 "请说点什么" print(e.send("你好")) # 输出 "收到:你好",返回下一轮的 "请说点什么" e.close() # 手动结束生成器

这里有个很多人不理解的地方:yield在生成器里不只是“返回值”,它还可以作为表达式接收外部传进来的值。第一次调用next(e)其实是在启动生成器,让代码执行到第一个yield位置;之后用send()传值,那个值会替代yield表达式的结果,赋给received变量。

throw()和close()就相对少用了。其中throw()可以在生成器内部主动抛一个异常,方便你控制内部状态;close()主动结束生成器。这些高级操作在实际数据处理流水线里会见到,但日常小脚本用得不多。不过理解它们的存在,对完整掌握迭代器体系很有帮助。

5. 真实项目里迭代器的应用场景与选型

5.1 惰性求值:处理海量数据、流式日志、超大文件

迭代器最核心的价值之一,就是惰性求值。所谓惰性求值,就是用到的时候才计算,不用不计算。这个特性在处理大数据时简直救命。

假设你要处理一个 10GB 的日志文件,如果直接全部读进内存,机器很可能直接卡死;但用迭代器一行一行读取,内存占用就保持在极低的水平。

def tail_lines(file_path, n=10): """模拟读取文件末尾的 n 行,用生成器逐行产出""" with open(file_path, "r", encoding="utf-8") as fp: # 先把指针挪到文件末尾 fp.seek(0, 2) end = fp.tell() pos = end buffer = [] while pos >= 0: pos -= 1 fp.seek(pos) char = fp.read(1) if char == "\n": buffer.reverse() line = "".join(buffer).strip() if line: yield line if len(buffer) >= n: # 控制只取最近 n 行 break buffer = [] else: buffer.append(char)

上面这段代码只是一个示例,它不会一次性把整个文件读进来,而是从末尾倒着找换行符,逐行产出。虽然实时性能不一定最优,但它诠释了“流式处理”的思想:大批量数据不需要全部驻留内存,边取边处理。

在真实的数据管道项目中,我也经常看到一种模式:第一层生成器从数据库游标取数据,第二层生成器做字段清洗,第三层做聚合统计。每一层都是生成器,数据像流水线一样一层一层往下流。如果数据量小你可能没感觉,一旦数据量到千万级别,这种设计的价值就完全体现出来了。

5.2 itertools:迭代器的“瑞士军刀”

Python 自带的itertools模块,提供了一整套基于迭代器的高效工具。我每次写数据处理代码,几乎都要用到其中的几个。

import itertools # 无限计数器,配合 takewhile 使用 for num in itertools.takewhile(lambda x: x < 10, itertools.count()): print(num, end=" ") # 0 1 2 3 4 5 6 7 8 9 print() # 分组相邻相同元素 for key, group in itertools.groupby("AAABBBCCAAA"): print(key, list(group)) # 输出: A ['A', 'A', 'A'] B ['B', 'B', 'B'] C ['C', 'C'] A ['A', 'A', 'A'] # 排列组合 for combo in itertools.combinations([1, 2, 3], 2): print(combo, end=" ") # (1, 2) (1, 3) (2, 3)

其中最容易被忽视的一个是itertools.chain(),它可以把多个可迭代对象串成一个,避免你用双重循环或者多次拼接,省内存又直观:

import itertools list_a = [1, 2, 3] list_b = ["a", "b", "c"] for item in itertools.chain(list_a, list_b): print(item, end=" ") # 1 2 3 a b c

itertools.product()在多层循环嵌套时也很好用。比如你有三个列表,想要得到它们的笛卡尔积,用普通三层嵌套很难看,但 product 一行就搞定。看代码:

import itertools colors = ["红", "绿", "蓝"] sizes = ["S", "M", "L"] for color, size in itertools.product(colors, sizes): print(f"{color}-{size}", end=" ") # 输出 红-S 红-M 红-L 绿-S 绿-M 绿-L 蓝-S 蓝-M 蓝-L

这些工具本质上都在帮你操作迭代器,所以你会越来越深刻地感觉到:整个 Python 的循环世界,其实都是建立在迭代器之上的。

5.3 自定义容器类时,到底选生成器还是类迭代器?

这个选择题,我在代码评审里看到过无数次,其实核心判断标准就两条:

  • 如果你只是想让某个类能被 for 遍历,并且遍历逻辑比较简单,优先用生成器函数,把__iter__实现成一个生成器方法,代码量少,语义清晰。
  • 如果你需要实现很复杂的遍历状态管理,比如遍历的同时要记录回溯路径、支持嵌套遍历、需要多个迭代器同时工作,那就要老老实实设计独立的迭代器类。

举例说明:

class SimpleCollection: def __init__(self, items): self.items = items def __iter__(self): # 用生成器实现 __iter__,简洁明了 for idx, item in enumerate(self.items): yield f"第{idx}个:{item}" sc = SimpleCollection(["a", "b", "c"]) for x in sc: print(x)

这种写法比手写独立迭代器类要轻松很多,而且不太会出错。很多时候,越简单越好,别为了演示技术而把代码写复杂。

6. 操作实录:一个真实项目里的迭代器问题排查

6.1 问题现场:为什么第二次统计结果全是 0?

有一次,我在处理一批网络请求日志时,写了一个统计模块,功能是从日志里读取每一行,提取状态码,统计各状态码的数量。第一次运行时结果正常,第二次运行却几乎全是 0。

当时的代码简化后大概长这样:

def load_lines(path): with open(path, "r", encoding="utf-8") as fp: for line in fp: yield line.strip() def analyze(path): lines = load_lines(path) total = 0 errors = 0 for line in lines: total += 1 if line.startswith("ERROR"): errors += 1 print(f"总数: {total}, 错误数: {errors}") # 第二次统计 total_2 = 0 errors_2 = 0 for line in lines: total_2 += 1 if line.startswith("ERROR"): errors_2 += 1 print(f"二次总数: {total_2}, 二次错误数: {errors_2}")

自己一看代码就发现问题了:lines是一个生成器,第一次 for 循环已经把它消费完了,第二次循环时,生成器内部已经StopIteration了,所以啥都拿不到。

当时排查过程让我印象深刻:一开始我还怀疑是数据处理逻辑的问题,后来打印了 lines 的类型才发现是 generator,才想起生成器的一次性特性。

6.2 排查思路与实际解法

遇到这种问题时,有几个惯用思路:

  • 先确认对象类型。type(lines)看一下到底是 list 还是 generator,如果是迭代器,就要注意它只能用一次。
  • 确认数据是否被消费过了。如果同一个变量被 for 循环遍历了两次,第二次拿不到数据,那大概率是迭代器。
  • 如果确实需要多次遍历,有两个选择:要么把生成器转成列表存起来,要么重新执行一次生成器函数来创建一个新对象。上面的代码里,直接把load_lines(path)的结果存成 list,问题就解决了,前提是数据量能接受。

我当时改完代码后,突然意识到一个很重要的点:很多新手根本不会考虑到“生成器是一次性的”这件事,是因为他们之前只跟列表打过交道,列表是拿来即用、重复可用的。这是一个思维习惯的转变。数据量小的时候,你完全可以无脑用 list;但数据量上来了,list 反而会成为压垮内存的元凶。这时候就得对迭代器的“一次性”特性有敬畏之心。

6.3 常见问题与避坑清单速查

我把日常项目里跟迭代器相关的坑整理成了一个速查表,每次写代码前扫一眼能省很多事:

问题场景典型表现解决办法
同一生成器被 for 循环两次第二次毫无输出重新调用生成器函数,或先存成 list
在迭代器上直接判断in判断完成后迭代器被消耗先转 list,或在迭代器还没开始消耗前做判断
自定义__iter__返回 self,嵌套遍历同一对象冲突两个 for 循环互相影响__iter__返回独立的迭代器对象
文件对象遍历完想再取一次数据第二次 readline 返回空先seek(0),或每次都重新打开文件
生成器函数内部抛异常异常信息里看不到具体数据行在生成器内部用 try/finally 包裹状态清理
用list(generator)包装一个大生成器内存暴涨不要强行 list 化,用 for 循环逐条处理

7. 几个实际场景里的进阶玩法与思考

7.1 用迭代器实现数据流式分页

分页是业务系统里最常见的需求。很多人习惯先把全量数据查出来,再在内存里切片分页。数据量少的时候没问题,一旦数据量以百万计,这个做法就很容易拖垮内存。

利用迭代器,可以做出一种“惰性分页”的效果:从数据库游标拿数据时,每次只拿一页,翻页时再按需取出下一页。这样页超码量级再大,内存占用也是恒定的。

我常用的一个简单抽象是这样的:

def paginate(query, page_size=10): page = 1 while True: offset = (page - 1) * page_size rows = query.fetch(limit=page_size, offset=offset) if not rows: break for row in rows: yield row page += 1

这个生成器会按页从数据源取数据,逐行产出。你遍历它的时候,根本不需要关心它在底层翻了多少页,只要像遍历列表一样取数据就行。

7.2 迭代器在消息系统里的优雅应用

在异步消息系统里面,迭代器的“惰性拉取”理念也很有意思。很多消息处理框架其实本质上都是遍历一个消息流。你写一个生成器,做消息拉取、解码、过滤,最后交给下层消费者打印或者入库,每一层各司其职,而且天然支持无限流。这让代码的结构变得非常干净。

我去年在做一个模拟项目X时,需要从多路订阅源里合并消息,用来生成统一时间线。最初我直接在循环里逐个调用不同源接口,代码又长又乱。后来改成用itertools.chain把所有消息源串成一个可迭代对象,再外加一个生成器做时间排序,整个代码一下子清爽了很多。那一刻我就意识到,迭代器不只是一种语法机制,它其实是一种组织代码的方式。

7.3 性能实测:列表 vs 生成器

有人一定会问:生成器那么省内存,是不是性能也会比列表好?其实这个要分开看。

单次遍历速度,列表往往比生成器快,因为生成器的暂停和恢复有额外开销。但是内存占用方面,列表是全量载入,生成器是逐条产出,当数据量大起来,内存就是决定生死的问题,速度反而不那么重要了。

我曾经用内存监控工具对比过两个场景:一个是把 1000 万个整数的平方全部存在 list 里,再求和;一个用生成器逐个计算求和。前者的内存占用可能高达几百 MB,后者几乎可以忽略不计。所以结论就是:如果你需要多次随机访问,用列表;如果你只需要逐一处理一次,用迭代器或生成器。

8. 最后再聊点个人体会

迭代器这个东西,初学的时候会觉得它不过是个语法细节,没必要深究。但当你真正接触大数据、流处理、自定义容器设计时,才会发现它其实是 Python 设计哲学里非常核心的一块。它把“遍历”这个再常见不过的动作抽象成一套协议,让不同的数据源都能用同一套方式被循环消费。这种统一性,正是 Python 能保持简洁优雅的原因之一。

我在实际工作中踩过的几次跟迭代器相关的坑,几乎都跟“迭代器是一次性的”这个特性有关。所以我也养成了一个习惯:凡是接收到一个不清楚底层是哪种类型的可迭代对象,先type()确认一下,再决定怎么下手。如果是生成器,我会有意识地避免重复遍历;如果确认要重复用,就直接转成列表。

平时写代码时,我也比较推荐优先考虑生成器而不是列表推导式,特别是你确认这个数据只会消费一次的情况下。虽然生成器写法一开始看起来不如列表推导式直观,但它在内存上的收益是实打实的。尤其是处理日志、读取大文件、处理网络流这类场景,生成器的优势几乎是碾压级别的。

如果你现在还是只把 for 循环当做一个“傻瓜式的遍历工具”,希望这篇文章能让你对它的底层机制有个新认知。下次写出for x in data的时候,你可以心里默默知道:Python 背地里正在调用iter(data)和next(迭代器),当一个接一个的值从迭代协议里流出来时,才是这个故事真正精彩的地方。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询