流畅的python14 章可迭代的对象、迭代器 和生成器

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

迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这就是迭代器模式(Iterator pattern)。

迭代器用于从集合中取出元素;而生成器用于“凭空”生成元素。通过斐波纳契数列能很好地说明二者之间的区别:斐波纳契数
列中的数有无穷个,在一个集合里放不下。不过要知道,在 Python社区中,大多数时候都把迭代器和生成器视作同一概念。

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) ?

?re.findall 函数返回一个字符串列表,里面的元素是正则表达式的
全部非重叠匹配。
? self.words 中保存的是 .findall 函数返回的结果,因此直接返回
指定索引位上的单词。
? 为了完善序列协议,我们实现了 __len__ 方法;不过,为了让对象
可以迭代,没必要实现这个方法。
? reprlib.repr 这个实用函数用于生成大型数据结构的简略字符串表
示形式。

示例 14-2 测试 Sentence 实例能否迭代

>>> s = Sentence(‘"The time has come," the Walrus said,‘) # ?
>>> s
Sentence(‘"The time ha... Walrus said,‘) # ?
>>> for word in s: # ?
... print(word)
The
time
has
come
the
Walrus
said
>>> list(s) # ?
[‘The‘, ‘time‘, ‘has‘, ‘come‘, ‘the‘, ‘Walrus‘, ‘said‘]

? 传入一个字符串,创建一个 Sentence 实例。
? 注意,__repr__ 方法的输出中包含 reprlib.repr 方法生成的
...。
? Sentence 实例可以迭代,稍后说明原因。
? 因为可以迭代,所以 Sentence 对象可以用于构建列表和其他可迭代
的类型。

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

解释器需要迭代对象 x 时,会自动调用 iter(x)。内置的 iter 函数有以下作用。

  • (1) 检查对象是否实现了 __iter__ 方法,如果实现了就调用它,获取一个迭代器。
  • (2) 如果没有实现 __iter__ 方法,但是实现了 __getitem__ 方法,Python 会创建一个迭代器,尝试按顺序(从索引 0 开始)获取元素。
  • (3) 如果尝试失败,Python 抛出 TypeError 异常,通常会提示“C objectis not iterable”(C 对象不可迭代),其中 C 是目标对象所属的类。任何 Python 序列都可迭代的原因是,它们都实现了 __getitem__ 方法。其实,标准的序列也都实现了 __iter__ 方法,因此你也应该这么做。之所以对 __getitem__ 方法做特殊处理,是为了向后兼容,而未来可能不会再这么做

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

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

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

下面是一个简单的 for 循环,迭代一个字符串。这里,字符串 ‘ABC‘是可迭代的对象。背后是有迭代器的,只不过我们看不到:

>>> s = ‘ABC‘
>>> for char in s:
... print(char)
...
A
B
C
###如果没有 for 语句,不得不使用 while 循环模拟,要像下面这样写:
>>> s = ‘ABC‘
>>> it = iter(s) # ?
>>> while True:
... try:
... print(next(it)) # ?
... except StopIteration: # ?
... del it # ?
... break # ?
...
A
B
C

? 使用可迭代的对象构建迭代器 it。
? 不断在迭代器上调用 next 函数,获取下一个字符。
? 如果没有字符了,迭代器会抛出 StopIteration 异常。
? 释放对 it 的引用,即废弃迭代器对象。
? 退出循环。

StopIteration 异常表明迭代器到头了。Python 语言内部会处理 for循环和其他迭代上下文(如列表推导、元组拆包,等等)中的StopIteration 异常。

标准的迭代器接口有两个方法。
__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__ 方法。这一版没有
                           #__getitem__ 方法,为的是明确表明这个类可以迭代,因为实现了
                        #__iter__ 方法。
        return SentenceIterator(self.words) #根据可迭代协议,__iter__ 方法实例化并返回一个迭代器。
class SentenceIterator:
    def __init__(self, words):
        self.words = words #SentenceIterator 实例引用单词列表。
        self.index = 0#self.index 用于确定下一个要获取的单词。
    def __next__(self):
        try:
            word = self.words[self.index] #获取 self.index 索引位上的单词。
        except IndexError:
            raise StopIteration() #如果 self.index 索引位上没有单词,那么抛出 StopIteration 异
常。
        self.index += 1
        return word
    def __iter__(self): #实现 self.__iter__ 方法。
        return self            

生成器函数

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

生成器函数的工作原理
只要 Python 函数的定义体中有 yield 关键字,该函数就是生成器函数。调用生成器函数时,会返回一个生成器对象。也就是说,生成器函数是生成器工厂。

惰性实现

目前实现的几版 Sentence 类都不具有惰性,因为 __init__ 方法急迫地构建好了文本中的单词列表,然后将其绑定到 self.words 属性上。
这样就得处理整个文本,列表使用的内存量可能与文本本身一样多(或许更多,这取决于文本中有多少非单词字符)。如果只需迭代前几个单词,大多数工作都是白费力气。
只要使用的是 Python 3,思索着做某件事有没有懒惰的方式,答案通常都是肯定的。

re.finditer 函数是 re.findall 函数的惰性版本,返回的不是列表,而是一个生成器,按需生成 re.MatchObject 实例。如果有很多匹配,re.finditer 函数能节省大量内存。我们要使用这个函数让第 4版 Sentence 类变得懒惰,即只在需要时才生成下一个单词。代码如示
例 14-7 所示。示例 14-7 sentence_gen2.py: 在生成器函数中调用 re.finditer生成器函数,实现 Sentence 类

import re
import reprlib
RE_WORD = re.compile(‘\w+‘)
class Sentence:
    def __init__(self, text):
        self.text = text
    def __repr__(self):
        return ‘Sentence(%s)‘ % reprlib.repr(self.text)
    def __iter__(self):
        for match in RE_WORD.finditer(self.text):
        yield match.group() 

? 不再需要 words 列表。
? finditer 函数构建一个迭代器,包含 self.text 中匹配 RE_WORD
的单词,产出 MatchObject 实例。
? match.group() 方法从 MatchObject 实例中提取匹配正则表达式的
具体文本。
生成器函数已经极大地简化了代码,但是使用生成器表达式甚至能把代
码变得更简短。

生成器表达式

生成器表达式可以理解为列表推导的惰性版本:不会迫切地构建列表,而是返回一个生成器,按需惰性生成元素。也就是说,如果列表推导是制造列表的工厂,那么生成器表达式就是制造生成器的工厂。

>>> def gen_AB(): # ?
... print(‘start‘)
... yield ‘A‘
... print(‘continue‘)
... yield ‘B‘
... print(‘end.‘)
...
>>> res1 = [x*3 for x in gen_AB()] # ?
start
continue
end.
>>> for i in res1: # ?
... print(‘-->‘, i)
...
--> AAA
--> BBB
>>> res2 = (x*3 for x in gen_AB()) # ?
>>> res2 # ?
<generator object <genexpr> at 0x10063c240>
>>> for i in res2: # ?
... print(‘-->‘, i)
...
start
--> AAA
continue
--> BBB
end.

? gen_AB 函数与示例 14-6 中的一样。
? 列表推导迫切地迭代 gen_AB() 函数生成的生成器对象产出的元
素:‘A‘ 和 ‘B‘。注意,下面的输出是 start、continue 和 end.。
? 这个 for 循环迭代列表推导生成的 res1 列表。
? 把生成器表达式返回的值赋值给 res2。只需调用 gen_AB() 函数,
虽然调用时会返回一个生成器,但是这里并不使用。
? res2 是一个生成器对象。
? 只有 for 循环迭代 res2 时,gen_AB 函数的定义体才会真正执
行。for 循环每次迭代时会隐式调用 next(res2),前进到 gen_AB 函
数中的下一个 yield 语句。注意,gen_AB 函数的输出与 for 循环中
print 函数的输出夹杂在一起。

使用生成器表达式实现 Sentence类

import re
import reprlib
RE_WORD = re.compile(‘\w+‘)
class Sentence:
    def __init__(self, text):
        self.text = text
    def __repr__(self):
        return ‘Sentence(%s)‘ % reprlib.repr(self.text)
    def __iter__(self):
        return (match.group() for match in RE_WORD.finditer(self.text))

这里不是生成器函数了(没有 yield),而是使用生成器表达式构建生成器,然后将其返回。不过,最终的效果一样:调用 __iter__ 方法会得到一个生成器对象。
生成器表达式是语法糖:完全可以替换成生成器函数,不过有时使用生成器表达式更便利。

标准库中的生成器函数

用于过滤的生成器函数

模块 函数 说明
itertools
compress(it,
selector_it)


并行处理两个可迭代的对象;如果 selector_it
中的元素是真值,产出 it 中对应的元素

itertools
dropwhile(predicate,
it)


处理 it ,跳过 predicate 的计算结果为真值的元
素,然后产出剩下的各个元素(不再进一步检
查)

内置 filter(predicate, it)
把 it 中的各个元素传给 predicate ,如果
predicate(item) 返回真值,那么产出对应的元
素;如果 predicate 是 None ,那么只产出真值元

itertools
filterfalse(predicate,
it)


与 filter 函数的作用类似,不过 predicate 的
逻辑是相反的: predicate 返回假值时产出对应
的元素

itertools
islice(it, stop) 或
islice(it, start,
stop, step=1)


产出 it 的切片,作用类似于 s[:stop] 或
s[start:stop:step] ,不过 it 可以是任何可迭代
的对象,而且这个函数实现的是惰性操作

itertools
takewhile(predicate,
it)


predicate 返回真值时产出对应的元素,然后立
即停止,不再继续检查

原文地址:https://www.cnblogs.com/chenxuming/p/9690444.html

时间: 2024-10-29 17:39:33

流畅的python14 章可迭代的对象、迭代器 和生成器的相关文章

第14章 可迭代的对象、迭代器和生成器

#第14章 可迭代的对象.迭代器和生成器 # 迭代是数据处理的基石.扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项.这就是迭代器模式(Iterator pattern). # 所有生成器都是迭代器,因为生成器完全实现了迭代器接口. # 迭代器用于从集合中取出元素:而生成器用于"凭空"生成元素. # 14.1 Sentence类第1版:单词序列 # 示例 14-1 sentence.py:把句子划分为单词序列 import re import r

流畅的python第十四章可迭代的对象,迭代器和生成器学习记录

在python中,所有集合都可以迭代,在python语言内部,迭代器用于支持 for循环 构建和扩展集合类型 逐行遍历文本文件 列表推导,字典推导和集合推导 元组拆包 调用函数时,使用*拆包实参 本章涵盖的话题 语言内部使用 iter(...) 内置函数处理可迭代对象的方式如何使用 Python 实现经典的迭代器模式详细说明生成器函数的工作原理如何使用生成器函数或生成器表达式代替经典的迭代器如何使用标准库中通用的生成器函数如何使用 yield from 语句合并生成器案例分析:在一个数据库转换工

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

迭代是数据处理的基石.扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项.这就是迭代器模式(Iterator pattern). p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 25.0px Helvetica } Sentence类第1版:单词序列 我们要实现一个 Sentence 类,以此打开探索可迭代对象的旅程.我们向这个类的构造方法传入包含一些文本的字符串,然后可以逐个单词迭代.第 1 版要实现序列协议,这

可迭代对象、迭代器与生成器

迭代器 什么是迭代器 迭代指的是一个重复的过程,且不是单纯的重复,每次重复都基于上次的结果. 迭代器指的是迭代取值的工具,该工具的特点是可以不依赖于索引取值. 为什么用迭代器 为了找出一种通用的&可以不依赖于索引的迭代取值方式. 如何用迭代器 可迭代的对象:但凡内置有__iter__方法得当对象都称之为可迭代的对象 迭代器对象:既内置有__iter__,又内置__next__方法 关于__iter__方法: 调用可迭代对象的__iter__会得到一个迭代器对象 调用迭代器对象的__iter__会

流畅python学习笔记:第十四章:迭代器和生成器

迭代器和生成器是python中的重要特性,本章作者花了很大的篇幅来介绍迭代器和生成器的用法. 首先来看一个单词序列的例子: import re re_word=re.compile(r'\w+') class Sentence(object):     def __init__(self,text):         self.text=text         self.word=re_word.findall(text)     def __getitem__(self, item):   

完全理解Python迭代对象、迭代器、生成器

在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式(list,set,dict comprehension)众多概念参杂在一起,难免让初学者一头雾水,我将用一篇文章试图将这些概念以及它们之间的关系捋清楚. 容器(container) 容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中.通常这类数据

[转载]完全理解Python迭代对象、迭代器、生成器

译文地址:liuzhijun 在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式(list,set,dict comprehension)众多概念参杂在一起,难免让初学者一头雾水,我将用一篇文章试图将这些概念以及它们之间的关系捋清楚. 容器(container) 容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素

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

在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式(list,set,dict comprehension)众多概念参杂在一起,难免让初学者一头雾水,我将用一篇文章试图将这些概念以及它们之间的关系捋清楚. 容器(container) 容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个地迭代获取,可以用in, not in关键字判断元素是否包含在容器中.通常这类数据

完全理解 Python 迭代对象、迭代器、生成器

2017/05/29 · 基础知识 · 9 评论 · 可迭代对象, 生成器, 迭代器 原文出处: liuzhijun 本文源自RQ作者的一篇博文,原文是Iterables vs. Iterators vs. Generators,俺写的这篇文章是按照自己的理解做的参考翻译,算不上是原文的中译版本,推荐阅读原文,谢谢网友指正. 在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式