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

目录

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

 一、Sentence类第一版:单词序列

①实现一个Sentence,以此开始探索可迭代对象。第一版要实现序列协议。

②测试Sentence是否可以迭代

③Sentence实例可以迭代的原因:

二、可迭代的对象与迭代器的对比

①可迭代的对象

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

③迭代器

三、Sentence类第二版:典型的迭代器

②把Sentence变成迭代器是一个坏主意

四、Sentence类第三版:生成器函数

②生成器函数的工作原理

五、把生成器当做协程


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

所有生成器都是迭代器,生成器完全实现了迭代器接口。迭代器从集合中取出元素,生成器用于’凭空‘生成元素。

生成器有广泛的用途,例如内置的range()函数返回一个类似于生成器的对象,而以前返回完整的列表。

在 Python 中, 所有集合都可以迭代。 在 Python 语言内部, 迭代器用于支持:

  1. for 循环
  2. 构建和扩展集合类型
  3. 逐行遍历文本文件
  4. 列表推导、 字典推导和集合推导
  5. 元组拆包
  6. 调用函数时, 使用 * 拆包实参
     

 一、Sentence类第一版:单词序列

①实现一个Sentence,以此开始探索可迭代对象。第一版要实现序列协议

import re
import reprlib

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

class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)  # 返回一个字符串列表,里面是正则表达式的全部非重叠匹配

    def __getitem__(self, index):
        return self.words[index]  # 返回指定索引位上的单词

    def __len__(self):  # 为了完善序列协议,实现了这个方法。单纯为了实现序列,这个方法没有必要
        return len(self.words)

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)  # 这个实用函数用于生成大型数据结构的简略字符串表示形式

②测试Sentence是否可以迭代

s = Sentence('"The time has come," the Walrus said,')
print(s) #输出结果是reprlib.repr方法生成的
for i in s:
    print(i)

Sentence实例可以迭代。

③Sentence实例可以迭代的原因:

  1. 检查对象是否实现了 __iter__ 方法, 如果实现了就调用它, 获取一个迭代器。
  2.  如果没有实现 __iter__ 方法, 但是实现了 __getitem__ 方法,Python 会创建一个迭代器, 尝试按顺序(从索引 0 开始) 获取元素。
  3.  如果尝试失败, Python 抛出 TypeError 异常, 通常会提示“C objectis not iterable”(C 对象不可迭代) , 其中 C 是目标对象所属的类。

任何 Python 序列都可迭代的原因是, 它们都实现了 __getitem__ 方法。 其实, 标准的序列也都实现了 __iter__ 方法。

二、可迭代的对象与迭代器的对比

①可迭代的对象

        使用 iter 内置函数可以获取迭代器的对象。 如果对象实现了能返回迭代器的 __iter__ 方法, 那么对象就是可迭代的。 序列都可以迭代; 实现了 __getitem__ 方法, 而且其参数是从零开始的索引, 这种对象也可以迭代。
        可迭代的对象和迭代器之间的关系: Python 从可迭代的对象中获取迭代器

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

__next__
        返回下一个可用的元素, 如果没有元素了, 抛出 StopIteration异常。
__iter__
        返回 self, 以便在应该使用可迭代对象的地方使用迭代器, 例如在 for 循环中。

③迭代器

迭代器是这样的对象: 实现了无参数的 __next__ 方法, 返回序列中的下一个元素; 如果没有元素了, 那么抛出 StopIteration 异常。Python 中的迭代器还实现了 __iter__ 方法, 因此迭代器也可以迭代。

三、Sentence类第二版:典型的迭代器

import re
import reprlib

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

class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

    def __iter__(self):  # 多了一个__iter__方法
        return SentenceIterator(self.words)  # 实例化并返回一个迭代器

class SentenceIterator:
    def __init__(self, words):
        self.words = words #引用单词列表
        self.index = 0 #用于确定下一个要获取的单词

    def __next__(self):
        try:
            word = self.words[self.index] #获取self.index索引位上的单词
        except IndexError:
            raise StopIteration() #索引完毕,抛出错误
        self.index += 1
        return word

    def __iter__(self):
        return self

②把Sentence变成迭代器是一个坏主意

可迭代的对象有个 __iter__ 方法, 每次都实例化一个新的迭代器; 而迭代器要实现 __next__ 方法, 返回单个元素, 此外还要实现__iter__ 方法, 返回迭代器本身。

可迭代的对象一定不能是自身的迭代器。 也就是说, 可迭代的对象必须实现 __iter__ 方法, 但不能实现 __next__ 方法。另一方面, 迭代器应该一直可以迭代。 迭代器的 __iter__ 方法应该返回自身。
 

四、Sentence类第三版:生成器函数

与上一版实现功能相同,但却更符合Python的习惯。使用生成器函数

import re
import reprlib

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


class Sentence:
    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)

    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)

    def __iter__(self):
        for word in self.words:  # 迭代self.words
            yield word  # 产出当前word
        return

②生成器函数的工作原理

只要Python函数中有yield关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。

 普通的函数与生成器函数在句法上唯一的区别是, 在后者的定义体中有 yield 关键字。

>>>def gen():
       yield 1
       yield 2
       yield 3
    
>>>g=gen()  #把 生成器对象赋值给g,g是迭代器
>>>next(g)
1
>>>next(g)
2
>>>next(g)
3
>>>next(g)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
StopIteration

>>>for i in gen():  #生成器是迭代器,会生成传给yield关键字的表达式的值。
       print(i)
1
2
3

生成器函数会创建一个生成器对象, 包装生成器函数的定义体。 把生成器传给 next(...) 函数时, 生成器函数会向前, 执行函数定义体中的下一个 yield 语句, 返回产出的值, 并在函数定义体的当前位置暂停。 最终, 函数的定义体返回时, 外层的生成器对象会抛出StopIteration 异常。

可以这个理解:遇到yield语句,返回产出的值,下一次调用next()后,从上次退出的地方接着执行。
 

def gen_AB():  
    print('start')
    yield 'A'  
    print('continue')
    yield 'B'  
    print('end.')  


for c in gen_AB():  
    print('-->', c)

上述执行顺序

五、把生成器当做协程

        与 .__next__() 方法一样, .send() 方法致使生成器前进到下一个yield 语句。 不过, .send() 方法还允许使用生成器的客户把数据发给自己, 即不管传给 .send() 方法什么参数, 那个参数都会成为生成器函数定义体中对应的 yield 表达式的值。 也就是说, .send() 方法允许在客户代码和生成器之间双向交换数据。 而 .__next__() 方法只允许客户从生成器中获取数据
        这是一项重要的“改进”, 甚至改变了生成器的本性: 像这样使用的话,生成器就变身为协程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值