Python生成器

生成器 Generators

要理解生成器,首先要理解迭代器,迭代器由以下三个部分组成:

  • 可迭代对象(iterable)
  • 迭代器(iterator)
  • 迭代(iteration)

1. 可迭代对象

只要定义了可以返回一个迭代器的__iter__方法的对象,或者定义了可以支持下标索引的__getitem__方法。

2. 迭代器

只要定义了一个__next__方法的对象。

3. 迭代

从某个地⽅(⽐如⼀个列表)取出⼀个元素的过程。当我们使⽤⼀个循环来遍历某个东西时,这就叫⼀个迭代。


生成器

生成器也是一种迭代器,但是只能对其迭代一次。因为生成器没有把所有的值存在内存中,而是在运行时生成值(lazy)。可以通过遍历来使用它们,使用for循环或传递给任意可以进行迭代的函数和结构。

大多数生成器是以函数来实现的,它们并不返回一个值,而是yield一个值。

生成器最好的使用场景是:不需要一次将所有计算的结果全部放在内存中,或者一次读出全部的原始数据,这样会造成很大的内存占用。

上述例子中,我们用for循环来迭代这个生成器函数,同样,我们可以使用python的next()函数来获得迭代器的下一个元素。

基础数据类型list和str都是可迭代对象,我们经常会使用for循环来获得每一个下标的数据。

但是我们发现,不能调用next来迭代字符串和数据,这是因为:

数组和字符串是可迭代对象,但不是迭代器。

使用for关键字迭代可迭代对象时,for会自动获取到迭代器,进行迭代。但是next只能对迭代器进行迭代,因此我们需要使用到iter()函数来根据一个可迭代对象返回一个迭代器对象!

同时,next方法没有实现对StopIteration异常的捕捉,在自己写的迭代器内一般需要额外进行迭代结束的编写。


测验:

现在我们要写一个生成器,迭代一个数组的时候,不再是按顺序取出而是隔一个元素取出。

1. 函数式迭代器(yield)

python 复制代码
def skip_elements(arr):
    for i in range(0, len(arr), 2):  # 从索引0开始,每次跨越2个元素
        yield arr[i]

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
gen = skip_elements(my_list)

for element in gen:
    print(element)

2. 编写一个迭代器:

python 复制代码
class SkipElementsGenerator:
    def __init__(self, arr):
        self.arr = arr
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.arr):
            raise StopIteration
        element = self.arr[self.index]
        self.index += 2  # 每次跳过一个元素
        return element

my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
gen = SkipElementsGenerator(my_list)

for element in gen:
    print(element)

SkipElementsGenerator是一个迭代器,我们直接将要迭代的数组当成形参丢入以初始化迭代器。

注意:因为要用for迭代迭代器,因此迭代器也必须得是可迭代对象,这里只需要简单实现__iter__函数并且返回self即可!

Another Way

比较直观了解可迭代对象与迭代器的例子:

python 复制代码
class MyArray:
    def __init__(self, arr):
        self.arr = arr

    def __iter__(self):
        return SkipElementsGenerator(self.arr)


class SkipElementsGenerator:
    def __init__(self, arr):
        self.arr = arr
        self.index = 0

    def __next__(self):
        if self.index >= len(self.arr):
            raise StopIteration
        element = self.arr[self.index]
        self.index += 2  # 每次跳过一个元素
        return element

# 创建自定义数组对象
my_list = MyArray([1, 2, 3, 4, 5, 6, 7, 8, 9])

# 使用生成器迭代元素
for element in my_list:
    print(element)

原始数据是可迭代对象,实现__iter__方法,返回它的迭代器。

迭代器实现__next__方法,迭代返回每一次迭代的值。

这个时候for循环直接迭代可迭代对象,不像上一个例子一样是迭代迭代器。

for循环是先找到可迭代对象的迭代器! 通过__iter__方法!

然后对迭代器进行迭代! 通过迭代器的__next__方法!

这样写的话可以实现函数的解耦,不会像上面代码一样,还要实例化迭代器gen = SkipElementsGenerator(my_list),然后人为迭代迭代器。

这样写的话,只需要像原来一个,实例化自定义的数据类型,直接用for或者iter来获得迭代器,代码中不会出现迭代器的名字,无感使用。

相关推荐
用户7783366132113 小时前
用 React Hook 封装搜索数据:useSerp 的防抖、缓存与错误处理
python·api
65岁退休Coder7 小时前
LangGraph v1.2.9 节点容错策略 & 流式输出 & 持久化记忆管理
后端·python·langchain
ikun_文9 小时前
Django框架路由Router的使用
python·pycharm·django
IvanCodes9 小时前
Python 基础语法(二):字符串与常用操作
python
昭昭日月明9 小时前
LangChain 生态:从链到代理,开发者需要掌握的三大核心
python·langchain·agent
Csvn10 小时前
🐍 Day 8:面向对象编程
后端·python
程序员天天困10 小时前
向量检索不准怎么办:混合检索与 Rerank 重排序召回优化实战
后端·python·ai编程
alphaTao11 小时前
LeetCode 每日一题 2026/8/24-2026/8/30
python·算法·leetcode
苏灿烤鱼11 小时前
当 AI Agent 遇见真实科学环境:深度拆解 Scientific Agent Skills,把"聊天机器人"变成"AI 科学家"
python·开源·agent
张文君11 小时前
ubuntu26.04坏道坏块分区隔离急速版260831-V0.12
linux·python