Python 笔记 — 迭代器和生成器

本文详细介绍了Python中的可迭代对象、迭代器以及生成器的概念、工作原理和应用场景,包括迭代的机制、如何判断对象是否可迭代,以及生成器函数、yield关键字的使用和优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、可迭代对象(Iterable)

1、迭代

对 list、tuple、str 等类型的数据使用 for…in… 的循环语法从其中依次拿到数据进行使用,这样的过程被称为遍历,也叫迭代

2、可迭代对象

可以通过 for…in… 这类语句迭代读取一条数据的对象称之为可迭代对象

列表元组字典字符串都是可迭代对象

整数浮点数布尔值都是不可迭代的。

3、满足条件

满足以下的条件的对象可以成为可迭代对象:

  • 对象实现了 __iter__ 方法。
  • __iter__ 方法返回了一个迭代器对象。

4、for 循环工作原理

在内部对可迭代对象调用 __iter__ 方法,获取到迭代器对象

再一次次的通过迭代器对象调用 __next__ 方法获取迭代结果

li = [1, 2, 3, 4]
for i in li:
	print(i)

for item in iterable:

先通过 iter() 函数获取可迭代对象 Iterable 的迭代器,然后对获取到的迭代器不断调用 next() 方法来获取下一个值,并将其赋值给 item,当遇到 StopIteration 的异常后循环结束。

5、判断迭代

可以使用 isinstance() 判断一个对象是否是 Iterable 对象。

from collections.abc import Iterable

print(isinstance([1, 2], Iterable))  # True
print(isinstance(5, Iterable))  # False
print(isinstance({1, 2}, Iterable))  # True
print(isinstance('abc', Iterable))  # True

可迭代对象的本质就是可以向我们提供⼀个这样的中间“人”。即迭代器帮助我们对其进行迭代遍历使用。

二、迭代器(Iterator)

迭代是 Python 最强大的功能之一,是访问集合元素的一种方式,可以记住遍历的位置的对象。

迭代器访问可迭代对象的工具,迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。

for 循环会返回对象的下一条数据,直到所有的都取完了。在这个过程中应该有一个“人”去记录到了第几条数据,把这个帮助我们进行数据迭代的“人”称为迭代器。

基本方法

iter() 和 next()

# 通过 iter() 内置函数取得可迭代对象的迭代器。
li = [1, 2, 3]  # 列表是可迭代对象
it = iter(li)  # 通过 iter() 方法取得 list 的迭代器

print(it)

# 运行结果:
# <list_iterator object at 0x0000023F8D239FF0>

可迭代对象其实都是 collections 模块里的 Iterable 类创建出来的实例的。比如一个列表,其实它就是 Iterable 类创建的实例对象。

功能

  • for 循环
  • 逐行遍历文本文件
  • 列表推导式
  • 元组拆包

特性

  • 访问者不需要关心迭代器内部结构,只需不断执行 next() 方法获取下一个内容。
  • 不能随机访问集合中的某个值,只能从头到尾顺序的读取。
  • 访问到一半时不能回退,不能访问之前的值。
  • 适合遍历很大的数据集合,节省内存,提升速度。

特点

可迭代:Python 中的迭代器实现了 __iter__ 方法,因此也可以迭代。

易耗损:迭代器经过一次依次取值的循环后就耗尽了,如果想再次迭代要重建迭代器。

优点

节约内存(循环过程中,数据不用一次读入,在处理文件对象时特别有用,因为文件也是迭代器对象)、不依赖索引取值、 实现惰性计算(需要时再取值计算)。

1、可迭代对象和迭代器

凡是可以 for 循环的,都属于 Iterable 可迭代对象

凡是可以 next() 的都是 Iterator 迭代器

from collections.abc import Iterable, Iterator

st = 'abc'
print(isinstance(st, Iterable))  # True
print(isinstance(st, Iterator))  # False

it = iter(st)  # 转换为迭代器
print(isinstance(it, Iterable))  # True
print(isinstance(it, Iterator))  # True

迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象。

可迭代对象包含迭代器。

如果一个对象拥有 __iter__ 方法,其是可迭代对象;如果一个对象拥有 __next__ 方法,其是迭代器。

定义可迭代对象,必须实现 __iter__ 方法;定义迭代器,必须实现 __iter____next__ 方法。 迭代器也是可迭代对象,因此迭代器必须也实现 __iter__ 方法。

联系:

Python 从可迭代的对象中获取迭代器。

区别:

可迭代的对象不是迭代器。

2、迭代器协议

概念

对象必须提供一个 __next__ 方法,执行该方法要么返回迭代中的下一项,要么就引起一个 stoplteration 异常,以终止迭代(只能往后走,不能往前退)。

条件

  • 对象实现了 __next__ 方法
  • __next__ 方法返回了某个数值(当然一般情况下,需要的是返回这个对象的特定的数字,并且按照一定的顺序进行依次返回)
  • __next__ 方法需要在值取完的时候,抛出 StopIteration 的错误信息。

可迭代对象

实现了迭代器协议的对象,对象内部定义一个 __iter__ 方法。

协议是一种约定,可迭代对象实现了迭代器协议,Python 的内部工具(如 for 循环,sum,min,max 函数等)使用迭代器协议访问对象。

可迭代对象是调用对象的 __iter__ 方法能够返回迭代器对象的一种对象。

3、应用场景

由于 Python 中没有“迭代器”这个类,因此具有以下两个特性的类都可以称为“迭代器”类:

  • __next__() 方法,返回容器的下一个元素或抛出 StopIteration 异常。
  • __iter__() 方法,返回迭代器本身。
# 创建一个返回数字的迭代器,初始值为 1,逐步递增 1
class A:
    def __iter__(self):
        self.a = 1
        # 该方法返回的是当前对象的迭代器类的实例
        return self  # 返回 self(即自己本身),表示自身即是自己的迭代器。

    def __next__(self):
        x = self.a
        self.a += 1
        return x

# 按照定义这个 Fib 实例化出来的对象就是迭代器对象,迭代器对象一定是可迭代对象。
my = A()
myiter = iter(my)
print(next(myiter))
print(next(myiter))
print(next(myiter))

# 运行结果:
# 1
# 2
# 3
# 不是迭代器对象的类
class Test:
    def __init__(self):
        self.n = 1

    def funa(self):
        self.n += 1
        return self.n

te = Test()
print(te.funa())

for i in te:
    print(i)  # 报错:TypeError: 'Test' object is not iterable
class Test:
    def __init__(self):
        self.n = 1
    def __iter__(self):  # 所有不可迭代的,加入 iter 之后,都变得可迭代
        return self  # 返回的是当前对象的迭代器类的实例
    def __next__(self):
        if self.n == 7:
            raise StopIteration('终止了')
        self.n += 1
        return self.n

# 按照定义这个 Test 实例化出来的对象就是迭代器对象,迭代器对象一定是可迭代对象。
te = Test()

print(te)  # <__main__.Test object at 0x0000011796EA7C40>
print(te.__iter__())  # <__main__.Test object at 0x0000011796EA7C40>

for i in te:  # for 循环的本质:循环所有对象,全都是使用迭代器协议。
    print(i)

# 运行结果:
# 2
# 3
# 4
# 5
# 6
# 7
  • for 循环可以遍历(字符串,列表,元祖,字典,集合,文件对象),那这些类型的数据肯定是可迭代对象。但是其实这些都不是可迭代对象,只不过在 for 循环中,调用了它们内部的 __iter__ 方法,把它们变成了可迭代对象。 然后 for 循环调用可迭代对象的 __next__ 方法取值,而且 for 循环会捕捉 stoplteration 异常,以终止迭代。
  • 可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其它的数据类型需要调用自己的内置的 __iter__ 方法),所以生成器就是可迭代对象。

三、生成器(Generator)

在 Python 中,使用了 yield 的函数被称为生成器。 跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器

1、生成器函数

常规函数的定义,但是,使用 yield 语句而不是 return 语句返回结果。

yield 语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

2、yield

效果就是使函数中断,并保存中断的状态,中断后,代码可以继续往下执行,过一段时间还可以重新调用这个函数,并且可以从上次 yield 的下面的一句代码开始执行,yield 可以返回值,也可以接收 send 来的参数。

在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值,并在下一次执行 next() 方法时从当前位置继续运行。

生成器也是一种迭代器,但是只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。

3、优点

(1)首先,生成器的好处是延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据量处理,将会非常有用。

(2)除了延迟计算,生成器还能有效提高代码可读性。

使用生成器的注意事项:生成器只能遍历一次

4、创建生成器

(1)生成器表达式

类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。

只要把一个列表生成式的[]改成()

# 列表推导式
li = [i*2 for i in range(3)] 
print(li)  # [0, 2, 4]

# 生成器表达式
li2 = (i*2 for i in range(3)) 
print(li2)  # <generator object <genexpr> at 0x000001E4C683C938>
print(next(li2))  # 0
print(next(li2))  # 2
print(next(li2))  # 4

(2)生成器函数

# 定义生成器,由 next 函数触发
def funa():
    print('---开始---')
    yield 'a'  # 返回一个 a, 并暂停函数
    yield 'b'

# 取值方式一:
fn = funa()  # 调用一个生成器函数,返回的是一个迭代器对象。
print(next(fn))

# 运行结果:
# ---开始---
# a

# 取值方式二:
for i in funa():
    print(i)
# 运行结果:
# ---开始---
# a
# b

for 循环中操作的对象是生成器中的 yied 生成的值,原因在于:

生成器是迭代器,会生成传给 yield 关键字的表达式的值。

注意:

不管有没有 return 语句,生成器函数都不会抛出 StopIteration 异常,而是在生成全部值后直接退出。

# 处理文件,用户指定要查找的文件和内容
# 将文件中包含要查找内容的每一行都输出到屏幕
def check(fname, word):
    with open(fname, encoding='utf-8') as file:
        for i in file:
            if word in i:
                yield i

ch = check('test.txt', '生成器')
print(next(ch))

5、return 和 yield

相同点:都是返回函数执行的结果 。

不同点:return 在返回结果后结束函数的运行,而 yield 则是让函数变成一个生成器,生成器每次产生一个值 (yield语句),函数被冻结,被唤醒后再产生一个值。

yield 生成器相比 return一次返回所有结果的优势:

  • 反应更迅速
  • 更节省空间
  • 使用更灵活

(1)生成器中,如果没有 return,则默认执行到函数完毕时返回 StopIteration。

def funa():
    yield 1

a = funa()
print(next(a))
print(next(a))

# 运行结果:
# StopIteration
# 1

(2)如果在 return 后返回一个值,那么这个值为 StopIteration 异常的说明,不是程序的返回值。

def funa():
    yield 1
    return '出现异常'

a = funa()
print(next(a))
print(next(a))

# 运行结果:
# StopIteration: 出现异常
# 1

记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值