Python可迭代对象/迭代器/生成器
概述
迭代是数据处理的基石.
扫描内存中放不下的数据集时, 需要找到一种惰性获取数据的方式, 即需要按需一次获取一个数据项. 这就是迭代器模式(Iterator pattern).
所有生成器都是迭代器, 因为生成器完全实现了迭代器接口.
在Python中所有集合都可迭代的. 在Python语言内部, 迭代器用于支持:
- for循环
- 构建和扩展集合类型
- 逐行遍历文本文件
- 列表推导/字典推导/集合推导
- 元组拆包
- 调用函数时, 使用*拆包实参
2.序列为何可以迭代?
解释器需要迭代对象x时, 会自动调用iter(x)函数.
内置的iter函数有以下作用:
- 检查对象是否实现了
__iter__
方法, 如果实现了就调用它, 获取一个迭代器; - 如果该对象没有实现
__iter__
方法, 但是实现了__getitem__
方法, Python会创建一个迭代器, 尝试按顺序(从索引0开始)获取元素; - 如果尝试失败, Python会抛出
TypeError
异常, 通常会提示C object is not iterable
(C对象不可迭代), 其中C是目标对象所属的类.
由上可知: 所有的序列都可迭代的原因---都实现了__getitem__
方法.
标准的序列也都实现了
__iter__
方法, 因此我们也应该这么做. 之所以对__getitem__
方法做特殊处理,是为了向后兼容.鸭子类型(duck typing)的极端形式: 不仅要实现特殊的
__iter__
方法,还要实现__getitem__
方法, 而且__getitem__
方法的参数是从0开始的整数(int), 才认为对象是可迭代的.白鹅类型(goose-typing)理论中: 如果实现了
__iter__
方法, 那么就认为对象是可迭代的.
检查对象x是否可迭代, 最准确的方法: 调用iter(x)
函数, 如果不可迭代将抛出TypeError
异常, 再来处理此异常.
可迭代的对象与迭代器的对比
可迭代的对象: 使用iter
内置函数可以获取迭代器的对象.
- 如果对象实现了能返回迭代器的
__iter__
方法.- 序列对象.
- 实现了
__getitem__
方法, 而且其参数是从零开始的索引.
可迭代对象与迭代器关系: Python从可迭代对象中获取迭代器.
StopIteration
异常表明迭代器到头了.
Python语言内部会处理for
循环和其他迭代器上下文(如列表推导/元组拆包,等等)中的StopIteration
异常.
标准迭代器接口:
-
__next__
: 返回下一个可用的元素, 如果没有元素了, 抛出StopIteration
异常. -
__iter__
: 返回self
, 即迭代器实例本身. 以便在应该使用可迭代对象的地方使用迭代器, 例如在for
循环中.
我们应该避免调用类似上述这种特殊方法, 使用next()
即可.
检查对象x是否是迭代器最好的办法: 调用isinstance(x, abc.Iterator)
. 得益于Iterator.__subclasshook__
方法, 即使对象x所属的类不是Iterator
类的真实子类或虚拟子类, 也能这样检查.
迭代器: 实现了无参数的__next__
方法, 返回序列中的下一个元素; 如果没有元素了, 那么抛出StopIteration
异常.
Python中的迭代器实现了__iter__
方法, 因此迭代器也可以迭代.
因为内置的
iter()
函数对序列做特殊处理. 接下来实现标准的迭代器协议
3.典型迭代器
# -*- coding: UTF-8 -*-
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 # index用于确定下一个要获取的单词
def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word
def __iter__(self):
return self
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
注意:
SentenceIterator
类的大多数代码都在处理迭代器内部状态.
可迭代对象实现了__iter__
方法, 每次都会实例化一个新的迭代器.
迭代器要实现__next__
方法, 调用next()
函数时返回单个元素, 还要实现__iter__
方法, 返回迭代器本身.
因此: 迭代器可以迭代, 但是可迭代的对象本身不是迭代器.
反模式: 在Sentence
类中实现__next__
方法, 让其实例既是可迭代的对象, 也是自身的迭代器.
可迭代的对象一定不能是自身的迭代器.
也就是: 可迭代的对象必须实现
__iter__
方法, 但不能实现__next__
方法.另一方面: 迭代器应该可以一直迭代. 迭代器的
__iter__
方法应该返回自身.
迭代器模式可用来:
- 访问一个聚合对象的内容而无需暴露它的内部表示
- 支持对聚合对象的多种遍历
- 为遍历不同的聚合结构提供一个统一的接口(即支持多态迭代)
为了"支持多种遍历", 必须能从同一个可迭代的实例中获取多个独立的迭代器, 而且各个迭代器要能维护自身的 内部状态, 因此这一模式正确的实现方法是: 每次调用
iter(my_iterable)
都新建一个独立的迭代器.这就是定义
SentenceIterator
类的原因.
4.生成器函数
# -*- coding: UTF-8 -*-
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 # 此return不是必须的.可以直接不写,直接返回None.不管有没有,都不会抛出StopIteration异常.
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
生成器函数: 只要Python函数的定义体中有yield
关键字, 该函数就是生成器函数.
调用生成器函数时, 会返回一个生成器对象. 即: 生成器函数是生成器工厂.
执行过程: 生成器函数会创建一个生成器对象, 包装生成器函数的定义体. 把生成器传给next()
函数时, 生成器函数会向前, 执行函数定义体中的下一个yield
语句, 返回产出的值, 并在函数定义体的当前位置暂停. 最终, 函数定义体返回时, 外层的生成器对象会抛出StopIteration
异常---与迭代器协议一致.
In [1]: def gen_123():
...: yield 1
...: yield 2
...: yield 3
...:
In [2]: gen_123
Out[2]: <function __main__.gen_123()>
In [3]: gen_123()
Out[3]: <generator object gen_123 at 0x10ce05c50>
In [4]: for i in gen_123():
...: print(i)
...:
1
2
3
In [5]: g = gen_123()
In [6]: next(g)
Out[6]: 1
In [7]: next(g)
Out[7]: 2
In [8]: next(g)
Out[8]: 3
In [9]: next(g)
---------------------------------------------------------------------------
StopIteration Traceback (most recent call last)
<ipython-input-9-e734f8aca5ac> in <module>
----> 1 next(g)
StopIteration:
In [10]:
生成器不会以常规的方式"返回"值: 生成器函数定义体中的
return
语句会触发生成器对象抛出StopIteration
异常.
In [10]: def gen_AB():
...: print("start")
...: yield 'A'
...: print("continue")
...: yield 'B'
...: print("end.")
...:
In [11]: for c in gen_AB():
...: print('-->', c)
...:
start
--> A
continue
--> B
end.
In [12]:
for
机制的作用与g = iter(gen_AB())
一样, 用于获取生成器对象, 然后每次迭代时调用next(g)
.
5.惰性实现
设计Iterator
接口时,考虑到了惰性: next(Iterator)
一次生成一个元素.
惰性求值(lazy evaluation)
及早求值(eager evaluation)
# -*- coding: UTF-8 -*-
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):
# finditer函数构建一个迭代器,包含self.text中匹配RE_WORD的单词,产出MatchObject实例
for match in RE_WORD.finditer(self.text):
# match.group()方法从MatchObject实例中提取匹配正则表达式的具体文本
yield match.group()
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
6.生成器表达式
生成器表达式可以理解为列表推导的惰性版本: 不会马上创建列表, 而是返回一个生成器, 按需惰性生成元素.
即: 如果列表推导是制造列表的工厂, 那么生成器表达式就是制造生成器的工厂.
In [1]: def gen_AB():
...: print('start')
...: yield 'A'
...: print('continue')
...: yield 'B'
...: print('end.')
...:
In [2]: res1 = [x*3 for x in gen_AB()]
start
continue
end.
In [3]: for i in res1:
...: print('-->', i)
...:
--> AAA
--> BBB
In [4]: res2 = (x*3 for x in gen_AB())
In [5]: res2
Out[5]: <generator object <genexpr> at 0x110f66258>
In [6]: for i in res2: # for循环每次迭代时,会隐式调用next(res2),前进到gen_AB函数的下一个yield语句
...: print('-->', i)
...:
start
--> AAA
continue
--> BBB
end.
由上可知,生成器表达式会产出生成器, 因此可以使用生成器表达式进一步减少Sentence
类的代码, 如下所示:
# -*- coding: UTF-8 -*-
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))
if __name__ == '__main__':
s = Sentence('"The time has come, " the Walrus said,')
print(s)
for word in s:
print(word)
print(list(s))
生成器表达式是语法糖: 完全可以替换成生成器函数, 不过有时使用生成器函数更便利.
7.何时使用生成器表达式
如果生成器表达式要分成多行写, 可以选择定义生成器函数, 以便提高可读性.
而且, 生成器函数有名称, 可以重用.
句法提示: 如果函数或构造方法只有一个参数, 传入生成器表达式时不用写一对调用函数的括号, 再写一对括号围住生成器表达式, 只写一对括号就行了. 然而, 如果生成器表达式后面还有其他参数, 那么必须使用括号围住,否则会抛出SyntaxError
异常.
8.等差数列生成器
典型的迭代器模式作用很简单-----遍历数据结构.
不过, 即便不是从集合中获取元素, 而是获取序列中即时生成的下一个值时, 也用得到这种基于方法的标准接口.
例如: 内置的range
函数用于生成有穷整数等差数列(Arithmetic Progression, AP); itertools.count
函数用于生成无穷等差数列.
# -*- coding: UTF-8 -*-
class ArithmeticProgression:
def __init__(self, begin, step, end=None):
self.begin = begin
self.step = step
self.end = end
def __iter__(self):
# 将self.begin的值赋值给result,不过先强制转换成前面的加法算式得到的类型.
result = type(self.begin + self.step)(self.begin)
forever = self.end is None
index = 0
while forever or result < self.end:
yield result
index += 1
result = self.begin + self.step * index
if __name__ == '__main__':
ap = ArithmeticProgression(0, 1, 3)
print(list(ap))
ap = ArithmeticProgression(1, .5, 3)
print(list(ap))
ap = ArithmeticProgression(0, 1/3, 1)
print(list(ap))
from fractions import Fraction
ap = ArithmeticProgression(0, Fraction(1, 3), 1)
print(list(ap))
from decimal import Decimal
ap = ArithmeticProgression(0, Decimal('.1'), .3)
print(list(ap))
上面这个类只是演示了如何使用生成器函数实现特殊的__iter__
方法.
然而, 如果一个类只是为了构建生成器而去实现__iter__
方法, 还不如使用生成器函数.
毕竟, 生成器函数是制造生成器的工厂.
# -*- coding: UTF-8 -*-
def aritprog_gen(begin, step, end=None):
result = type(begin + step)(begin)
forever = end is None
index = 0
while forever or result < end:
yield result
index += 1
result = begin + step * index
if __name__ == '__main__':
ap = aritprog_gen(0, 1, 3)
print(list(ap))
ap = aritprog_gen(1, .5, 3)
print(list(ap))
ap = aritprog_gen(0, 1/3, 1)
print(list(ap))
from fractions import Fraction
ap = aritprog_gen(0, Fraction(1, 3), 1)
print(list(ap))
from decimal import Decimal
ap = aritprog_gen(0, Decimal('.1'), .3)
print(list(ap))
上述即使用生成器函数来实现的.
使用itertools
模块生成等差数列
itertools.count(start, step)
示例:
In [7]: import itertools
In [8]: gen = itertools.count(1, .5)
In [9]: next(gen)
Out[9]: 1
In [10]: next(gen)
Out[10]: 1.5
In [11]: next(gen)
Out[11]: 2.0
In [12]: next(gen)
Out[12]: 2.5
然而, itertools.count
函数从不停止. 如果调用list(count())
, Python会创建一个特别大的列表, 超出可用内存, 在调用失败之前, 电脑会疯狂地运转.
itertools.takewhile
函数则不同. 它会生成一个使用另一个生成器的生成器, 在指定的条件计算结果为False时停止. 因此, 可以把这两个函数结合在一起使用. 如下所示:
In [13]: gen = itertools.takewhile(lambda n: n < 3, itertools.count(1, .5))
In [14]: list(gen)
Out[14]: [1, 1.5, 2.0, 2.5]
利用takewhile
和count
函数, 编写的代码流畅而简短, 如下所示:
# -*- coding: UTF-8 -*-
import itertools
def aritprog_gen(begin, step, end=None):
first = type(begin + step)(begin)
ap_gen = itertools.count(first, step)
if end is not None:
ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
return ap_gen
if __name__ == '__main__':
ap = aritprog_gen(0, 1, 3)
print(list(ap))
ap = aritprog_gen(1, .5, 3)
print(list(ap))
ap = aritprog_gen(0, 1/3, 1)
print(list(ap))
from fractions import Fraction
ap = aritprog_gen(0, Fraction(1, 3), 1)
print(list(ap))
from decimal import Decimal
ap = aritprog_gen(0, Decimal('.1'), .3)
print(list(ap))
上述示例中的aritprog_gen
不是生成器函数, 因为定义体中并没有yield
关键字.
但是它会返回一个生成器, 因此和其他生成器函数一样, 也是生成器工厂函数.
注意: 实现生成器时要知道标准库中有什么可用, 否则很可能重复造轮子.
9.标准库中的生成器函数
第一组是用于过滤的生成器函数:从输入的可迭代对象中产出元素的子集,而且不修改元素本身.
下表中的大多数函数都接受一个断言参数(predicate). 这个参数是个布尔函数, 有一个参数, 会应用到输入中的每个元素上, 用于判断元素是否包含在输出中.
模块 | 函数 | 说明 |
---|---|---|
itertools |
compress(it, selector_it) |
并行处理两个可迭代的对象; 如果selector_it 中的元素是真值, 产出it 中对应的元素. |
itertools |
dropwhile(predicate, it) |
处理it , 跳过predicate 的计算结果为真值的元素, 然后产出剩下的各个元素(不再进一步检查) |
内置 | filter(predicate, it) |
把it 中的每个元素传给predicate , 如果返回真值, 那么产出对应的元素; 如果predicate 为None, 那么只产出真值元素 |
itertools |
filterfalse(predicate, it) |
与filter 函数类似.不过是相反的: predicate 返回假值时产出对应的元素 |
itertools |
islice(it, stop) 或islice(it, start, stop, step=1)
|
产出it 的切片, 作用类似于s[:stop] 或s[start:stop:step] , 不过it 可以是任何可迭代的对象, 而且这个函数实现的是惰性操作 |
itertools |
takewhile(predicate, it) |
predicate 返回真值时产出对应的元素, 然后立即停止, 不再继续检查. |
示例如下:
In [15]: def vowel(c):
...: return c.lower() in 'aeiou'
...:
In [16]: list(filter(vowel, 'Aardvark'))
Out[16]: ['A', 'a', 'a']
In [17]: import itertools
In [18]: list(itertools.filterfalse(vowel, 'Aardvark'))
Out[18]: ['r', 'd', 'v', 'r', 'k']
In [19]: list(itertools.dropwhile(vowel, 'Aardvark'))
Out[19]: ['r', 'd', 'v', 'a', 'r', 'k']
In [20]: list(itertools.takewhile(vowel, 'Aardvark'))
Out[20]: ['A', 'a']
In [21]: list(itertools.compress('Aardvark', (1,0,1,1,0,1)))
Out[21]: ['A', 'r', 'd', 'a']
In [22]: list(itertools.islice('Aardvark', 4, 7))
Out[22]: ['v', 'a', 'r']
In [23]: list(itertools.islice('Aardvark', 1, 7, 2))
Out[23]: ['a', 'd', 'a']
下一组是用于映射的生成器函数: 在输入的单个可迭代对象(map
和starmap
函数处理多个可迭代的对象)中的各个元素上做计算, 然后返回结果.
下表中的生成器函数会从输入的可迭代对象中的各个元素中产出一个元素. 如果输入来自多个可迭代的对象, 第一个可迭代的对象到头后就停止输出.
模块 | 函数 | 说明 |
---|---|---|
itertools |
accumulate(it, [func] |
产出累积的总和; 如果提供了func , 那么把前两个元素传给它, 然后把计算结果和下一个元素传给它, 以此类推, 最后产出结果. |
内置 | enumerate(iterable, start=0) |
产出由两个元素组成的元组, 结构是(index, item) , 其中index 从start 开始计数, item 则从iterable 中获取 |
内置 | map(func, it1, [it2,...,itN] |
把it 中的各个元素传给func , 产出结果; 如果传入N个可迭代的对象, 那么func 必须能接受N个参数, 而且要并行处理各个可迭代的对象 |
itertools |
starmap(func, it) |
把it 中的各个元素传给func , 产出结果; 输入的可迭代对象应该产出可迭代的元素iit , 然后以func(*iit) 这种形式调用func
|
itertools.accumulate
示例:
In [24]: sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
In [25]: import itertools
In [26]: list(itertools.accumulate(sample)) # 计算总和
Out[26]: [5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
In [27]: list(itertools.accumulate(sample, min)) # 计算最小值
Out[27]: [5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
In [28]: list(itertools.accumulate(sample, max)) # 计算最大值
Out[28]: [5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
In [29]: import operator
In [30]: list(itertools.accumulate(sample, operator.mul)) # 计算乘积
Out[30]: [5, 20, 40, 320, 2240, 13440, 40320, 0, 0, 0]
In [31]: list(itertools.accumulate(range(1, 11), operator.mul)) # 计算各数阶乘
Out[31]: [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
映射生成器函数示例:
In [32]: list(enumerate('albatroz', 1)) # 以1开始,为单词中的字母编号
Out[32]:
[(1, 'a'),
(2, 'l'),
(3, 'b'),
(4, 'a'),
(5, 't'),
(6, 'r'),
(7, 'o'),
(8, 'z')]
In [33]: import operator
In [34]: list(map(operator.mul, range(11), range(11))) # 从0到10,计算各个数的平方
Out[34]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
In [35]: list(map(operator.mul, range(11), [2, 4, 8])) # 元素最少的可迭代对象到头后停止
Out[35]: [0, 4, 16]
In [36]: list(map(lambda x, y: (x, y), range(11), [2, 4, 8])) # 相当于zip函数
Out[36]: [(0, 2), (1, 4), (2, 8)]
In [37]: list(itertools.starmap(operator.mul, enumerate('albatroz', 1)))
Out[37]: ['a', 'll', 'bbb', 'aaaa', 'ttttt', 'rrrrrr', 'ooooooo', 'zzzzzzzz']
In [38]: sample = [5, 4, 2, 8, 7, 6, 3, 0, 9, 1]
In [39]: list(itertools.starmap(lambda a, b: b/a, enumerate(itertools.accumulate(sample), 1)))
Out[39]:
[5.0,
4.5,
3.6666666666666665,
4.75,
5.2,
5.333333333333333,
5.0,
4.375,
4.888888888888889,
4.5]
下面一组是用于合并的生成器函数, 这些函数都从输入的多个可迭代对象中产出元素.
-
chain
和chain.from_iterable
按顺序(一个接一个)处理输入的可迭代对象 -
product
/zip
和zip_longest
并行处理输入的各个可迭代对象.
模块 | 函数 | 说明 |
---|---|---|
itertools |
chain(it1, ..., itN) |
先产出it1 中的所有元素, 然后产出it2 中的所有元素, 以此类推, 无缝衔接在一起 |
itertools |
chain.from_iterable(it) |
产出it 生成的各个可迭代对象中的元素, 一个接一个, 无缝衔接; it 应该产出可迭代的元素, 例如可迭代的对象列表 |
itertools |
product(it1, ..., itN, repeat=1) |
计算笛卡尔积: 从输入的各个可迭代对象中获取元素, 合并成由N个元素组成的数组, 与嵌套的for循环一样; repeat 指明重复处理多少次输入的可迭代对象 |
内置 | zip(it1, ..., itN) |
并行从输入的各个可迭代对象中获取元素, 产出由N个元素组成的元组, 只要有一个可迭代对象到头了, 就默默停止 |
itertools |
zip_longest(it1, ..., itN, fillvalue=None) |
并行从输入的各个可迭代对象中获取元素, 产出由N个元素组成的元组, 等到最长的可迭代对象到头后停止, 空缺的值使用fillvalue 填充. |
zip
函数的名称出自zip fastener
或zipper
(拉链, 与ZIP压缩没有关系).
chain
及zip
生成器函数及其同胞使用示例:
In [40]: import itertools
In [41]: list(itertools.chain('ABC', range(2))) # 通常传入两个或多个可迭代对象
Out[41]: ['A', 'B', 'C', 0, 1]
In [42]: list(itertools.chain(enumerate('ABC'))) # 如果仅传入一个,则没什么效果
Out[42]: [(0, 'A'), (1, 'B'), (2, 'C')]
In [43]: list(itertools.chain.from_iterable(enumerate('ABC')))
Out[43]: [0, 'A', 1, 'B', 2, 'C']
In [44]: list(zip('ABC', range(5))) # 常用于将两个可迭代对象合并成一系列由两个元素组成的元组
Out[44]: [('A', 0), ('B', 1), ('C', 2)]
In [45]: list(zip('ABC', range(5), [10, 20, 30, 40]))
Out[45]: [('A', 0, 10), ('B', 1, 20), ('C', 2, 30)]
In [46]: list(itertools.zip_longest('ABC', range(5)))
Out[46]: [('A', 0), ('B', 1), ('C', 2), (None, 3), (None, 4)]
In [47]: list(itertools.zip_longest('ABC', range(5), fillvalue='?'))
Out[47]: [('A', 0), ('B', 1), ('C', 2), ('?', 3), ('?', 4)]
10.yield from语法
如果生成器函数需要产出另一个生成器生成的值, 传统解决方法是使用嵌套for循环.
例如, 自己实现itertools.chain
:
In [1]: def chain(*iterable):
...: for it in iterable:
...: for i in it:
...: yield i
...:
In [2]: s = 'ABC'
In [3]: t = tuple(range(3))
In [4]: list(chain(s, t))
Out[4]: ['A', 'B', 'C', 0, 1, 2]
如果使用yield from
语法, 则是如下形式:
In [1]: def chain(*iterable):
...: for i in iterable:
...: yield from i
...:
In [2]: s = 'ABC'
In [3]: t = tuple(range(3))
In [4]: list(chain(s, t))
Out[4]: ['A', 'B', 'C', 0, 1, 2]
yield from
完全代替了内层的for循环.除了代替for循环外,
yield from
还会创建通道, 把内层生成器直接与外层生成器的客户端联系起来.把生成器当成协程使用时, 这个通道特别重要, 不仅能为客户端代码产生值, 还能使用客户端代码提供的值.
11.可迭代的归约函数
归约函数(合拢函数/累加函数): 接受一个可迭代对象, 返回单个结果.
其实,这里每个列出的函数都可以使用
itertools.reduce
函数实现, 内置是因为它们便于解决常见的问题.此外对于
all
和any
函数来说, 有一项优化措施是reduce
函数做不到的: 短路(即一旦确定了结果就立即停止使用迭代器).
模块 | 函数 | 说明 |
---|---|---|
内置 | all(it) |
it 中所有元素为真时返回True, 否则返回False; all([]) 返回True |
内置 | any(it) |
只要it 中有元素为真值就返回True, 否则返回False; any([]) 返回False |
内置 | max(it, [key=, ][default=]) |
返回it 中最大的元素; key是排序函数, 与sorted函数中的一样; 如果可迭代的对象为空, 返回default |
内置 | min(it, [key=, ][default=]) |
返回it 中最小的元素; 其他同上 |
functools |
reduce(func, it, [initial]) |
把前两个元素传给func, 然后把计算结果和第三个元素传给func, 以此类推, 返回最后的结果; 如果提供了initial, 把他当做第一个元素传入 |
内置 | sum(it, start=0) |
it 中所有元素的和. 如果提供可选的start, 会把它加上 |
12.深入分析iter函数
鲜为人知的用法: 传入两个参数, 使用常规的函数或任何可调用的对象创建迭代器.
- 第一个参数必须是可调用的对象, 用于不断调用(没有参数)产出各个值
- 第二个参数是哨符, 是个标记值, 当可调用的对象返回这个值时, 触发迭代器抛出
StopIteration
异常, 而不产出哨符.
示例如下:
In [1]: from random import randint
In [2]: def d6():
...: return randint(1, 6)
...:
In [3]: d6_iter = iter(d6, 1)
In [4]: d6_iter
Out[4]: <callable_iterator at 0x108c05c50>
In [5]: for roll in d6_iter:
...: print(roll)
...:
5
In [6]:
内置函数iter
文档中给了一个很好的例子: 逐行读取文件, 直到遇到特定的行为止.
with open('mydata.txt') as fp:
for line in iter(fp.readline, ''):
process_line(line)
网友评论