迭代器
通常,数据分析重要的部分是以自动化的方式一遍又一遍地重复类似的计算。 例如,您可能有一张名单表,要将其分割成名字和姓氏,或者是日期表要将其转换成某种标准格式的日期。 Python 给出的解决方案一是迭代器语法。 我们已经通过 range 迭代器看到了这一点:
for i in range(10):
print(i, end=' ')
0 1 2 3 4 5 6 7 8 9
在这里,我们将进行更深入的研究。 事实上,在Python 3中,range 不是一个列表,而是一个称为迭代器的东西,了解它的工作方式是理解各种非常有用的Python功能的关键。
遍历列表
在遍历列表的具体情景下,最容易理解迭代器。 考虑以下:
for value in [2, 4, 6, 8, 10]:
# do some operation
print(value + 1, end=' ')
3 5 7 9 11
熟悉的“ for x in y”语法使我们可以对列表中的每个值重复执行某些操作。 该代码的语法非常接近其英语描述("for [each] value in [the] list"),这只是使Python成为一种易于学习和使用的直观语言的语法选择之一。
但是,表面上语句的行为并不是真正发生的事情。 当您编写像"for val in L"的内容时,Python解释器会检查它是否具有迭代器接口,自己可以使用内置的iter 函数检查:
iter([2, 4, 6, 8, 10])
<list_iterator at 0x104722400>
正是此迭代器对象提供了for循环所需的功能。 iter对象是一个容器,它使您可以在下一个对象有效时对其进行访问,可以通过内置函数 next 看到该对象:
I = iter([2, 4, 6, 8, 10])
print(next(I))
2
print(next(I))
4
print(next(I))
6
这种间接设计有什么目的呢? 然而,事实证明这是非常有用的,因为它允许 Python 将事物视为实际上不是列表的列表。
range():像列表并不总是列表
这种间接迭代的最常见示例可能是 Python 3 中的 range() 函数(在Python 2中名为 xrange()),该函数不返回列表,而是返回一个特殊的range() 对象:
range(10)
range(0, 10)
range(像列表一样)向外公开了一个迭代器:
iter(range(10))
<range_iterator at 0x1045a1810>
因此,Python知道将其视为列表:
for i in range(10):
print(i, end=' ')
0 1 2 3 4 5 6 7 8 9
间接迭代器的好处是,不会显式创建完整列表! 我们可以通过 range 计算来看到这一点,如果我们实际实例化了它,将会耗尽我们的系统内存(请注意,在Python 2中,range 创建了一个列表,因此运行以下代码情况会非常糟糕!):
N = 10 ** 12
for i in range(N):
if i >= 10: break
print(i, end=', ')
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
如果 range 实际上创建了一个1万亿个值的列表,那么它将占用数十TB的机器内存:这是一种浪费!考虑到我们忽略了除前10个值之外的所有值。
实际上,根本没有理由迭代器必须结束! Python的 itertools 库包含一个充当无限范围的count函数:
from itertools import count
for i in count():
if i >= 10:
break
print(i, end=', ')
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
如果在这里我们没有循环中断 break 语句,那么它将继续愉快地计数,直到该过程被手动中断或终止(例如使用ctrl-C)为止。
有用的迭代器
这种迭代器语法几乎在 Python 内置类型以及更多数据科学特定对象中广泛使用,我们将在后面的部分中探讨。 在这里,我们将介绍 Python 语言中一些更有用的迭代器:
enumerate
通常,您不仅需要迭代数组中的值,而且还需要跟踪索引。 您可能会想这样做:
L = [2, 4, 6, 8, 10]
for i in range(len(L)):
print(i, L[i])
0 2
1 4
2 6
3 8
4 10
尽管这确实可行,但是 Python 提供的 enumerate 迭代器使语法更简洁:
for i, val in enumerate(L):
print(i, val)
0 2
1 4
2 6
3 8
4 10
这是枚举列表中索引和值的更“ Pythonic”方式。
zip
在其他时候,您可能要同时迭代多个列表。 您当然可以像我们之前看过的非Pythonic示例那样遍历索引,但是最好使用zip迭代器,它将可迭代的项链在一起:
L = [2, 4, 6, 8, 10]
R = [3, 6, 9, 12, 15]
for lval, rval in zip(L, R):
print(lval, rval)
2 3
4 6
6 9
8 12
10 15
可以将任意数量的可迭代项链在一起,如果长度不同,则最短者将确定拉链的长度。
map 和 filter
map 迭代器接收一个函数并将其应用于迭代器中的值:
# find the first 10 square numbers
square = lambda x: x ** 2
for val in map(square, range(10)):
print(val, end=' ')
0 1 4 9 16 25 36 49 64 81
filter 迭代器看起来很相似,除了filter 函数返回 True,filter 迭代器才会传递值:
# find values up to 10 for which x % 2 is zero
is_even = lambda x: x % 2 == 0
for val in filter(is_even, range(10)):
print(val, end=' ')
0 2 4 6 8
map和filter函数以及reduce函数(位于Python的functools模块中)是函数式编程风格的基本组成部分,尽管在Python世界中不是占主导地位的编程风格,但它具有直言不讳的支持者(例如,请参见 ,pytoolz库)。
迭代器作为函数参数
我们在 *args和**kwargs:灵活的参数 中看到了。 * args和** kwargs可用于将序列和字典传递给函数。 事实证明,* args语法不仅适用于序列,而且适用于任何迭代器:
print(*range(10))
0 1 2 3 4 5 6 7 8 9
因此,例如,我们可以使用一个小技巧将前面的map示例压缩为以下内容:
print(*map(lambda x: x ** 2, range(10)))
0 1 4 9 16 25 36 49 64 81
使用这个技巧,我们可以回答Python学习者论坛中一个古老的问题:为什么没有 unzip() 与 zip() 功能相反的函数呢? 如果您将自己锁在一个黑暗的壁橱中并考虑了一会儿,您可能会意识到 zip() 的反面就是... zip()! 关键是 zip() 可以将任意数量的迭代器或序列链在一起。 观察:
L1 = (1, 2, 3, 4)
L2 = ('a', 'b', 'c', 'd')
z = zip(L1, L2)
print(*z)
(1, 'a') (2, 'b') (3, 'c') (4, 'd')
z = zip(L1, L2)
new_L1, new_L2 = zip(*z)
print(new_L1, new_L2)
(1, 2, 3, 4) ('a', 'b', 'c', 'd')
思考一会。 如果您了解它为什么起作用,那么您已经很深入的理解Python迭代器了!
专用迭代器:itertools
我们简要介绍了无限范围迭代器itertools.count。 itertools模块包含大量有用的迭代器。 值得您花时间探索该模块以查看可用的功能。 例如,考虑itertools.permutations函数,该函数遍历序列的所有排列:
from itertools import permutations
p = permutations(range(3))
print(*p)
(0, 1, 2) (0, 2, 1) (1, 0, 2) (1, 2, 0) (2, 0, 1) (2, 1, 0)
同样,itertools.combinations函数迭代列表中N个值的所有唯一组合:
from itertools import combinations
c = combinations(range(4), 2)
print(*c)
(0, 1) (0, 2) (0, 3) (1, 2) (1, 3) (2, 3)
与乘积迭代器有关的是 product,它迭代两个或多个可迭代对象之间的所有配对:
from itertools import product
p = product('ab', range(3))
print(*p)
('a', 0) ('a', 1) ('a', 2) ('b', 0) ('b', 1) ('b', 2)
itertools 中存在更多有用的迭代器:完整列表以及一些示例可在Python的在线文档中找到。
本文来自翻译如下文章,仅用于学习
原文:
https://nbviewer.jupyter.org/github/jakevdp/WhirlwindTourOfPython/blob/master/10-Iterators.ipynb