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

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

一、简介

1、关于迭代器和生成器

关于python的迭代器和生成器这两个概念,大多数程序员都认为其在功能上是类似的,在python官方文档中也有时认为迭代器就是生成器,其实这两种还是有一定的区别。

使用内置函数iter()可以生成迭代器,使用内置函数next()可以获得迭代器中的值。

第一点,从概念上
在典型的迭代器设计模式中,迭代器用于遍历集合,从中产出元素,迭代器不能修改数据源中的值,只能原封不动地产出,而生成器可能无需遍历就能生成值。

第二点,从接口方面
python的迭代器协议定义了两个方法__iter____next__,生成器实现了这两个方法,因此从这方面来看,所有的生成器都是迭代器。

第三点、从实现方面
生成器这种python语言结构可以使用两种方式编写:1、含有yield关键字的函数,称之为生成器函数。2、使用生成器表达式。

2、关于可迭代对象

为什么是可迭代的?

在介绍可迭代对象之前,我们先了解一下python中序列可迭代的原因,序列之所以可以迭代是因为iter()内置函数,iter()内置函数有以下作用:

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

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

(3) 如果上述尝试均失败,python会抛出TypeError异常

什么是可迭代对象?

按照上面python序列可迭代的原因来看,Python中任意的对象,只要它定义了可以返回一个迭代器的 __iter__ 方法,或者定义了可以支持下标索引的 __getitem__ 方法,那么它就是一个可迭代对象。简单说,可迭代对象就是能提供迭代器的任意对象。

二、可迭代对象

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

按照上面我们对可迭代对象的定义,我们进行如下代码实验

import re
import reprlib

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

class Sentence(object):

    def __init__(self, text):
        self.text = text
        self.words = RE_WORD.findall(text)  # re.findall函数返回一个字符串列表,得到匹配的单词
    
    # 我们只实现了__getitem__方法
    def __getitem__(self, index):
        return self.words[index]
    
    def __repr__(self):
        return 'Sentence(%s)' % reprlib.repr(self.text)
    
s = Sentence('My favorite language is python !')
print(s)
# 我们迭代打印s中的值,打印成功则Sentence类是可以迭代的
for word in s: print(word)
Sentence('My favorite ...e is python !')
My
favorite
language
is
python

在上述代码实验中,我们并没有重载__itet__方法,而是重载了__getitem__方法,我们自定义的Sentence类仍然是可以迭代的,说明我们之前对可迭代对象的定义正确。

我们来验证是否能够使用iter()内置函数来直接获取对象的迭代器,

s1 = Sentence('Hi Python')
it = iter(s1)  # 获取序列的迭代器对象
print(next(it))
print(next(it))
Hi
Python

结果表明,我能确实能够获得序列对象的迭代器。

三、迭代器

标准的迭代器需要实现的方法:

(1) __next__ 返回下一个可用的元素,如果没有元素,抛出StopIteration异常。

(2) __iter__ 返回self,以便在应该使用可迭代对象的地方使用迭代器,例如for循环中。
在这里插入图片描述

现在我们按照迭代的标准写法,来改写前一个列子的代码。

import re
import reprlib

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

class Sentence(object):  # 可迭代对象实现__iter__

    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):
        return SentenceIterator(self.words)
    
class SentenceIterator(object):  # 迭代器实现__next__和__iter__

    def __init__(self, words):
        self.words = words
        self.index = 0
    
    def __next__(self):
        try:
            word = self.words[self.index]
        except IndexError:
            raise StopIteration
        self.index += 1
        return word
    
    def __iter__(self):
        return self

s1 = Sentence('My favorite language is Python!')
print(s1)
print('================================')

for word in s1: print(word)

it = iter(s1)
print('================================')
print(next(it))
print(next(it))

Sentence('My favorite ...ge is Python!')
================================
My
favorite
language
is
Python
================================
My
favorite

在构建迭代器对象和迭代器时经常会出现错误,原因是混淆了两者,要知道,可迭代的对象有一个__iter__方法,每次都实例化一个新的迭代器;而迭代器要实现__next__方法,返回单个元素,此外还要实现__iter__方法,返回迭代器本身。

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

四、生成器与生成器函数

1、生成器函数

先介绍什么是生成器函数,在普通函数定义中,不需要return,而是采用yield关键字来“产生”值,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象,也就是说生成器函数是生成器工厂。

定义一个简单的生成器函数:

def gen_123():
    for i in [1,2,3]:
        yield i

it = gen_123()
print(next(it))
print(next(it))
print(next(it))
1
2
3

上述代码中,为了实现相同的功能,符合python编程风格的做法是,用生成器函数来代替SentenceIterator类

import re
import reprlib

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

class Sentence(object):  # 可迭代对象实现__iter__

    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:
            yield word

s2 = Sentence('My favorite language is Pythooooon!')
print(s2)
print('================================')

for word in s2: print(word)
print('================================')

it = iter(s2)
print(next(it))
print(next(it))
Sentence('My favorite ...s Pythooooon!')
================================
My
favorite
language
is
Pythooooon
================================
My
favorite

注意我们在此,并没有定义迭代器类,而是使用生成器函数来代替迭代器的类,更加pythonic

2、生成器表达式

生成器表达式,和列表生成式字典生成式等类似,只不过将[ ]替换成( )

it = (x for x in [1,2,3,4])
print(type(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
<class 'generator'>
1
2
3
4

五、Reference

https://www.liaoxuefeng.com/wiki/1016959663602400/1017318207388128
https://www.runoob.com/python3/python3-iterator-generator.html
https://py.eastlakeside.cn/book/DataStructures/generators.html
《FluentPython》

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值