从迭代器到生成器

迭代器?

你有没有想过在python里for i in lit遍历一个列表,他究竟干了什么,为什么有的变量可以循环,而有的不可以for遍历?就比如说for i in 2,对一个数字遍历会报错TypeError: 'int' object is not iterable,这句话意思是int对象不是迭代器,看来想要迭代,还必须要有一定的属性,而这个属性就是__iter__以及__next__成员方法。

现在我用一个学生对象来举例迭代器,假设我想用for循环打印该学生的所有属性值:

  1. 第一步:对学生类创建__iter__成员方法
    该方法用于返回迭代器,你可以直接返回一个可迭代对象比如list或元组等内置对象,也可以返回self自己本身,只不过返回self本身,你就需要写__next__函数的逻辑。
  2. 第二步:添加__next__方法
    如果返回self本身作为迭代器就需要__next__方法,这个方法用于填写返回值的逻辑以及什么时候遍历终止。在迭代完毕时,需要抛出StopIteration来告知没有元素可以迭代!
python 复制代码
class Student:
    def __init__(self, name, age, stu_id, grade):
        # 初始化学生属性
        self.name = name    # 姓名
        self.age = age      # 年龄
        self.stu_id = stu_id  # 学号
        self.grade = grade  # 成绩

    def __iter__(self):
        # 迭代器初始化方法:返回迭代器对象本身
        # 定义一个索引,用来遍历属性列表
        self.index = 0
        # 把对象的所有属性存入列表(固定遍历顺序)
        self.attributes = [self.name, self.age, self.stu_id, self.grade]
        return self

    def __next__(self):
        # 迭代器核心:每次调用返回下一个属性
        if self.index < len(self.attributes):
            # 获取当前属性
            value = self.attributes[self.index]
            # 索引+1,为下一次迭代做准备
            self.index += 1
            return value
        # 迭代完毕,抛出停止迭代异常
        raise StopIteration


# 测试代码
if __name__ == '__main__':
    # 创建学生对象
    stu = Student("张三", 18, "2025001", 95)

    # 迭代遍历学生所有属性(for循环自动调用迭代器)
    print("学生所有属性:")
    for attr in stu:
        print(attr)

    # 手动调用迭代器(验证手写迭代器生效)
    print("\n手动迭代属性:")
    iter_obj = iter(stu)
    print(next(iter_obj))
    print(next(iter_obj))
    print(next(iter_obj))
    print(next(iter_obj))

所以定义迭代器有什么用,只是遍历?

迭代器在读取文件时大有用途,比如读取一个大型文件,你的目的是一行一行读取,在需要时读取下一行,于是你认为读取到列表里是不错的选择,但是发现一下子加载到内存就会有很大开销,于是迭代器登场了:

python 复制代码
class LineReader:
    def __init__(self, file_path):
        # 打开文件
        self.file = open(file_path, 'r', encoding='utf-8')
        # 标记是否已经读取完毕
        self.is_finished = False

    def __iter__(self):
        # 迭代器返回自身
        return self

    def __next__(self):
        # 如果已经读完,直接抛出停止迭代
        if self.is_finished:
            self.file.close()  # 关闭文件
            raise StopIteration

        # 读取一行
        line = self.file.readline()

        # 如果读到空字符串,说明文件结束
        if not line:
            self.is_finished = True
            self.file.close()
            raise StopIteration

        # 去掉换行符并返回
        return line.strip()


# ===================== 测试 =====================
if __name__ == '__main__':
    # 先创建一个测试文件 test.txt
    with open('test.txt', 'w', encoding='utf-8') as f:
        f.write("第一行:我是学生\n")
        f.write("第二行:学习Python\n")
        f.write("第三行:手写迭代器\n")
        f.write("第四行:逐行读取文件\n")

    # 使用我们的逐行读取迭代器
    print("=== 逐行读取内容 ===")
    reader = LineReader("test.txt")

    # for 循环自动迭代
    for line in reader:
        print(line)

这个迭代器可以实现一行一行读取文件,而且内存开销也不大,读完时也会自动关闭文件。

生成器函数

如果上面读取文件的代码太复杂,生成器也是不错选择。

生成器函数里必须有yield,每次遍历生成器,yield都会返回一个生成的内容,并且函数阻塞到这里,等到下次调用该生成器,会从这里继续执行后面的代码。
生成器生成1-9的例子

python 复制代码
def test():
  a=1
  while a<10:
      yield a
      a+=1
for i in test():
  print(i)

他的逻辑是这样的:for循环先从生成器拿到第一个值1,此时生成器阻塞在a+=1处,当for循环打印1后,再次从生成器取值,生成器从阻塞的地方继续执行,a+=1后yield返回2以此类推.注意下面是错误的写法:

python 复制代码
def test():
    a=1
    if a<10:
        yield a
        a+=1
for i in test():
    print(i)

生成器遍历文件

python 复制代码
def read_file(file_path):
    f = open(file_path, 'r', encoding='utf-8')
    while True:
        line = f.readline()
        if not line:
            break
        yield line
    f.close()

# 遍历
for line in read_file("test.txt"):
    print(line)
相关推荐
AC赳赳老秦1 小时前
网安工程师提效:用 OpenClaw 实现漏洞扫描报告生成、安全巡检自动化、日志合规审计
java·开发语言·前端·javascript·python·deepseek·openclaw
你数过天上的星星吗1 小时前
Python学习笔记二(函数、类与对象)
笔记·python·学习
四维迁跃1 小时前
如何排查SQL存储过程死锁_分析死锁日志与索引优化
jvm·数据库·python
m0_741173331 小时前
如何检测SQL注入风险_利用模糊测试技术发现漏洞
jvm·数据库·python
xcbrand1 小时前
餐饮品牌全案公司哪家可靠
运维·python
2401_846339561 小时前
CSS如何解决Less与CSS兼容性问题_通过配置文件实现平滑过渡与混合开发
jvm·数据库·python
qq_413847401 小时前
CSS如何控制全屏显示的元素样式
jvm·数据库·python
scan7241 小时前
上下文摘要
python
第一程序员1 小时前
2026年GitHub上最火的10个Python项目,Rust开发者必看
python·rust·github