Python可迭代对象、迭代器和生成器

本文深入探讨了Python中的可迭代对象、迭代器和生成器的概念。讲解了如何实现可迭代对象,如何判断一个对象是否可迭代或迭代器,以及迭代器接口的两个关键方法。介绍了生成器函数和生成器表达式的使用,强调了它们的惰性特性和内存效率。文章还讨论了标准库中的各种生成器函数,如`itertools`模块的功能,并展示了如何实现等差数列生成器的多种方法。最后,文章提到了`yield from`关键字以及将生成器作为协程处理的概念。

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

迭代是数据处理的基石。

内存中放不下的数据集,我们要找到一种惰性获取数据的方式,即一次获取一个数据项,这就是迭代器模式(iterator pattern)。

在Python2.2加入了yield关键字,这个关键字用于构建生成器(generator),其作用和迭代器一样。

所有的生成器都是迭代器。

因为生成器完全实现了迭代器接口。

迭代器用于从集合中取出元素。

生成器用于“凭空”生成元素。比如斐波那契数列中的数有无数个,在集合中放不过下。

在Python社区中,大多数时候都把迭代器和生成器视为同一概念。

迭代器用于支持:for循环、构架你和扩展集合类型、逐行遍历文本文件、列表推导;字典推导;集合推导、元组拆包、调用函数的*拆包实参。

实现可迭代对象:Sentence类

实现一个Sentence类,把句子拆分为单词序列。

import re
import reprlib

RE_WORD = re.compile(r'\w+')


class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)  # 返回一个列表,元素是匹配项

    def __getitem__(self, item):
        return self.words[item]

    def __len__(self):  # 完善序列协议,实现了len方法。可迭代对象,没必要实现这个
        return len(self.words)

    def __repr__(self):
        class_name = type(self).__name__
        return "{}({})".format(class_name, reprlib.repr(self.text))  # 大型数据结构的省略字符串表示形式


s = Sentence('we love you')

print(s)
for word in s:
    print(word)
打印
Sentence('we love you')
we
love
you

知识点:

  1. reprlib.repr前面章节提到过,是以省略号...表示过多的元素。

序列可迭代的原因:iter函数

解释器在调用可迭代对象x时,会自动调用iter(x)

内置的iter函数有以下作用:

  1. 检查对象是否实现了__iter__方法,如果实现了就调用它,获取一个迭代器。

  1. 如果没有实现__iter__方法,但是实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素。

  1. 如果以上都调用失败,Python会抛出TypeError异常,提示 X Object yes not Iterable

任何的Python序列可迭代的原因,它们都实现了__iter__方法或者至少实现了__getitem__方法。

这是鸭子类型的极端形式,只是实现了__iter__或者实现__getitem__方法且此方法的参数是从0开始的证书,就认为对象是可迭代的。

不需要创建子类,也不用注册虚拟子类,因为abc.Iterable类实现了__subclasshook__方法

示例,只要实现了__iter__方法,就是Iterable子类

from collections import abc


class Foo:
    def __iter__(self):
        pass


f = Foo()
print(isinstance(f, abc.Iterable))
print(issubclass(Foo, abc.Iterable))
打印
True
True

判断是否可迭代对象

要注意的是,虽然Sentence类是可迭代的,但是不过通过issubclass(Sentence, abc.Iterable)的测试,因为没有实现__iter__方法。

在Python3.4开始,检查一个对象是否可迭代,最准确的方法是调用iter(x)函数,如果不可迭代,再处理TypeError异常。

这比使用isinstance(x, abc.Iterable)更准确,因为iter(x)函数会考虑到遗留的__getitem__方法,而abc.Iterable类则不考虑。

因为只要实现了__getitem__方法就是可迭代的,不一定要实现__iter__。

可迭代的对象和迭代器

可迭代的对象定义:

使用iter函数可以获取迭代器的对象。如果对象实现了能返回迭代器的__iter__方法,那么对象就是可迭代的。

序列都可以迭代:实现了__getitem__方法,而且其参数是从零开始的索引,这种对象也可以迭代。

要明确可迭代对象和迭代器的关系:Python从可迭代对象中获取迭代器。

比如下面的for循环,迭代一个字符串。这里的"ABC"就是可迭代对象,背后有迭代器,只是我们看不到,for是迭代上下文,自动帮我们处理了:

>>> for char in "ABC":

... print(char)

...

A

B

C

如果不适用for语句,使用while模拟:

s = "ABC"

it = iter(s)

while True:

try:

print(next(it))

except StopIteration:

del it

break

知识点:

  1. 可迭代对象使用iter()函数变为迭代器。

  1. 使用next函数不断的在迭代器中获取下一个字符。

  1. 如果没有字符了,迭代器抛出StopIteration异常。表明迭代器已经到头了。Python会处理for循环、列表推导、元组拆包等待迭代器上下文中的StopIteration异常

标准的迭代器接口有两个方法。

  • __next__:返回下一个可用元素,如果没有元素了,抛出StopIteration异常

  • __iter__:返回self,以便在应该使用可迭代对象的地方使用迭代器,比如在for循环中

这个接口在collection.abc.Iterator抽象基类中制定。这个类定义了__next__抽象方法。这个类继承自Iterable类,__iter__抽象方法是在Iterable类中定义。

关系图:

示例,abc.Iterator类源码

from collections import Iterable
from abc import abstractmethod


class Iterator(Iterable):
    __slots__ = ()

    @abstractmethod
    def __next__(self):
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            if any("__next__" in B.__dict__ for B in C.__mro__) and any("__iter__" in B.__dict__ for B in C.__mro__)
                return True
        return NotImplemented

在Python3中,Iterator抽象基类定义的抽象方法是it.__next__();在Python2中是it.next()。我们应该避免直接调用特殊方法,使用iter(it)即可,在Python2和3中都能使用。

因为迭代器只需要实现__next__和__iter__两个方法,所以除了调用next()函数,然后捕获StopIteration异常之外,没有别的办法能检查是否还有遗留的元素。

也没有办法能还原迭代器。如果想要再次迭代,只能再调用iter()传入之前的可迭代对象。传入迭代器本身是没用的,因为Iterator.__iter__方法的实现是返回实例本身。

迭代器定义:

迭代器是这样的对象:

1.实现了无参数的__next__方法,返回序列中的下一个元素;

2.如果没有元素了,会抛出StopIteration异常;

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

判断是否迭代器

检查对象x是否为迭代器的最好方式是调用isinsta

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值