Python迭代工具有哪些及怎么使用

range

它的功能比乍看起来要强大的多。

start和stop

使用range()的第一个要点是理解参数:range(start, stop). start代表我们从start数字开始计数,包含start这个数字;stop代表我们在到达stop数字后停止计数,但不包括stop数字!

所以,如果我们有range(1, 10),就会得到[1, 2, 3, 4, 5, 6, 7, 8, 9]。开始于1,但不能到10

如果我们想包含10在我们的序列中,我们需要range(1, 11): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

顺便说一句,如果我们只指定一个参数,例如range(10),它将假定范围的开始是0。在这种情况下,我们会得到[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]. 当range()用于控制传统的 for 循环时,你会经常看到以这种方式使用它。

step

我最喜欢range()的技巧是它的可选第三个参数:当你指定 range(start, stop, step)时,该step参数允许你每次增加大于1的值。

可以用在实现打印出 前100个7 的所有倍数,从7自身到700,包括在内:range(7, 701, 7)会这样做。(请注意,我最后指定了701,以确保包括700在内。)

也可以用在打印所有小于 100的奇数: range(1, 100, 2)

return

如果你正在尝试使用range,打印出7的所有倍数,你可能会注意到结果并没有达到你的预期:

sevens = range(7, 701, 7)
print(sevens)

print 命令打印文字短语range(7, 701, 7)。发现并不是我们想要的!

请记住,range()返回一个类似于迭代器的对象(python 2x返回的是列表)。要将其完全存储为列表,我们需要通过将其包装在函数list()中来将其转换为列表:

sevens = list(range(7, 701, 7))
print(sevens)

现在输出就是我们想要的——前100个 7 的倍数的列表!

分割

在我们进入所有这些新的迭代优点之前,我想向你介绍扩展索引符号,它使我们能够更强大地从有序容器中选择元素,例如列表。

说到列表,来看看下面这个:

dossiers = ['Sir Vile', 'Dr. Belljar', 'Baron Grinnit', 'Medeva', 'General Mayhem', 'Buggs Zapper', 'Jacqueline Hyde', 'Jane Reaction', 'Dee Cryption']

不管你是否意识到,你已经知道正常的索引符号。

print(dossiers[1])
>>> Dr. Belljar

这返回了容器的第二个元素(索引1) 。dossiers很简单,对吧?几乎所有语言都提供这种行为。

那么,如果我们想要第二个和第三个元素呢?

print(dossiers[1:3])
>>> ['Dr. Belljar', 'Baron Grinnit']

刚才发生了什么?在扩展索引表示法中,我们有三个参数,用冒号分隔:startstopstep。嘿,看起来很熟悉对吧?这些是range()使用的相同参数!它们的工作方式也完全相同。(当然,我们在上面的示例中省略了第三个参数 [ step]。)

请注意,该示例打印出Dr. Belljar(index 1) 和Baron Grinnit(index 2),但不是 Medeva,因为stop参数被迫停止了。

千万要注意,start一定要小于 stop,你才能得到结果!不过有一个例外,我们稍后会谈到。

现在,如果你想要从第二个开始的所有其他元素怎么办?

print(dossiers[1::2])
>>> ['Dr. Belljar', 'Medeva', 'Buggs Zapper', 'Jane Reaction']

你会注意到我们没有指定stop. 因为我们其实不需要!扩展索引符号允许你省略任何参数,只要你有冒号来分隔开。由于省略了第二个参数,我们只是将多余的:放在它本来应该的位置之后。

反向分割

扩展索引符号使(start, stop, step)逻辑更进一步,允许你反向分割!一开始这有点绕脑筋,但是请坚持住……

print(dossiers[-1])

打印出列表中的最后一项。负数代表从列表末尾开始计数!这感觉有点奇怪,因为我们习惯于从0索引开始计数,但负零并不是真正的有意义的数,所以我们从-1开始。

鉴于此,我们如何打印最后三个项目?我们可能会这样尝试,但它实际上不起作用......

print(dossiers[-1:-4])
>>> []

这将返回一个空列表。为什么?请记住,start必须小于 stop,即使在使用负索引时也是如此。所以,我们必须把-4作为我们的start,因为-4 < -1

print(dossiers[-4:-1])
>>> [&#39;Buggs Zapper&#39;, &#39;Jacqueline Hyde&#39;, &#39;Jane Reaction&#39;]

这更接近了,但仍然存在问题。Dee Cryption是我们的最后一个元素,它在哪里呢?记住,stop是停止计数的数字,且不包含当前数字。但我们不能只用dossiers[-4],因为那只会给我们Buggs Zapper。并且dossiers[-4:-0]是无效。

要解决这个问题的方法就是,告诉 Python 我们明确省略了第二个参数:在我们的第一个参数后面加上一个冒号

print(dossiers[-4:])
>>> [&#39;Buggs Zapper&#39;, &#39;Jacqueline Hyde&#39;, &#39;Jane Reaction&#39;, &#39;Dee Cryption&#39;]

现在我们得到了最后一个元素。如果我们想要最后三个,那就把-4改为-3......

print(dossiers[-3:])
>>> [&#39;Jacqueline Hyde&#39;, &#39;Jane Reaction&#39;, &#39;Dee Cryption&#39;]

如果我们在第三个参数step中输入一个负数,你认为会发生什么?让我们尝试-1在它前面加上两个冒号,表示我们想要整个列表。

print(dossiers[::-1])
>>> [&#39;Dee Cryption&#39;, &#39;Jane Reaction&#39;, &#39;Jacqueline Hyde&#39;, &#39;Buggs Zapper&#39;, &#39;General Mayhem&#39;, &#39;Medeva&#39;, &#39;Baron Grinnit&#39;, &#39;Dr. Belljar&#39;, &#39;Sir Vile&#39;]

看起来这是将所有内容都逆向打印除了!事实上, step设置为-1颠倒了列表。

现在让我们尝试-2...

print(dossiers[::-2])
>>> [&#39;Dee Cryption&#39;, &#39;Jacqueline Hyde&#39;, &#39;General Mayhem&#39;, &#39;Baron Grinnit&#39;, &#39;Sir Vile&#39;]

这不仅反转了列表,而且跳过了所有部分元素。负数的step行为与正数完全相同step,只是它逆向工作!

那么,如果我们想把所有东西放在一起怎么办?也许我们想以相反的顺序列出第二个、第三个和第四个元素......

print(dossiers[2:5:-1])
>>> []

注意: startstop必须按顺序遍历。如果step为正,则start必须小于stop;但是,如果step为负数,则start必须大于stop

你可以把它想象成摄影之旅的步行路线。step告诉你走哪条路,你的步幅应该有多大。一到就开始拍照start,一遇到问题就stop收起相机。

所以,要解决这个问题,我们需要交换我们的startand stop

print(dossiers[5:2:-1])
>>> [&#39;Buggs Zapper&#39;, &#39;General Mayhem&#39;, &#39;Medeva&#39;]

说明: Python 还提供了slice()anditertools.islice()函数,它们的行为方式大致相同。但是,它们都比扩展索引符号更受限制,因此你最好使用扩展索引符号而不是函数。

操作迭代对象

我们将在本节中探索的其余函数都与迭代对象一起使用。虽然我将在大多数示例中使用列表,但请记住,你可以使用任何可迭代的对象,包括range()函数。

all和any

想象一下,你在一个可迭代的容器(例如列表)中获得了一大堆数据,例如包含数百个名称的列表。在将该列表输入到你的超级算法之前,你希望通过检查每个元素中是否存在异常值来节省一些处理时间。

这就是all函数的用途。

dossiers = [&#39;Sir Vile&#39;, &#39;Dr. Belljar&#39;, &#39;Baron Grinnit&#39;, &#39;Medeva&#39;, &#39;General Mayhem&#39;, &#39;Buggs Zapper&#39;, &#39;&#39;, &#39;Jane Reaction&#39;, &#39;Dee Cryption&#39;]
print(all(dossiers))
>>> False

你可能还记得,空字符串 ( '')在 Python 中的计算结果为False。该all()函数评估每个元素,并确保它返回True。如果甚至一个计算结果为False,该all()函数也将返回 false。

any()以几乎相同的方式工作,但是它只需要一个元素来评估True.

乍一看,这些似乎不是很有用,但是当与其他一些工具结合使用时,甚至与列表解析(稍后部分)结合使用时,它们可以节省大量时间!

enumerate

在循环中,如果你需要访问列表的及其索引,你可以使用该enumerate()函数来完成。

foo = [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;, &#39;D&#39;, &#39;E&#39;]

for index, value in enumerate(foo):
    print(f&#39;Element {index} is has the value {value}.&#39;)

但是enumerate(),不仅限于列表。像所有这些其他函数一样,它适用于任何可迭代的对象,需要返回编号(或枚举)的每个值。例如,我们把它用在range()。让我们用它来打印从 10 到 100 ( range(10,101,10)) 的每一个 10 的倍数。下面是举例...

for index, value in enumerate(range(10,101,10)):
    print(f&#39;Element {index} is has the value {value}.&#39;)

这给了我们...

Element 0 is has the value 10.
Element 1 is has the value 20.
Element 2 is has the value 30.
Element 3 is has the value 40.
Element 4 is has the value 50.
Element 5 is has the value 60.
Element 6 is has the value 70.
Element 7 is has the value 80.
Element 8 is has the value 90.
Element 9 is has the value 100

我们可以从中得出一个简洁的模式,但我们必须从1而不是0开始枚举。果然,我们可以通过传递起始计数数字作为第二个参数来实现。我们还将稍微调整我们输出的信息,简化一下输出格式。

for index, value in enumerate(range(10,101,10), 1):
    print(f&#39;{index} times 10 equals {value}&#39;)

当我们运行它时,我们得到...

1 times 10 equals 10
2 times 10 equals 20
3 times 10 equals 30
4 times 10 equals 40
5 times 10 equals 50
6 times 10 equals 60
7 times 10 equals 70
8 times 10 equals 80
9 times 10 equals 90
10 times 10 equals 100

filter

假设我们正在跟踪在一堆地点找到的线索数量,将它们存储在字典中。对于这个例子,我将在最后一节中借用和调整字典......

locations = {
    &#39;Parade Ground&#39;: 0,
    &#39;Ste.-Catherine Street&#39;: 0,
    &#39;Pont Victoria&#39;: 0,
    &#39;Underground City&#39;: 3,
    &#39;Mont Royal Park&#39;: 0,
    &#39;Fine Arts Museum&#39;: 0,
    &#39;Humor Hall of Fame&#39;: 2,
    &#39;Lachine Canal&#39;: 4,
    &#39;Montreal Jazz Festival&#39;: 1,
    &#39;Olympic Stadium&#39;: 0,
    &#39;St. Lawrence River&#39;: 2,
    &#39;Old Montréal&#39;: 0,
    &#39;McGill University&#39;: 0,
    &#39;Chalet Lookout&#39;: 0,
    &#39;Île Notre-Dame&#39;: 0
    }

也许我们需要找到所有有线索的地点,而忽略其余的。我们将首先编写一个函数来测试特定的键值元组对。这似乎是一个荒谬的过度复杂化,但一会儿会有用:

def has_clues(pair):
    return bool(pair[1])

我们将字典中的每一对作为元组提交给函数,pair[1]将会是值(例如('Underground City', 3))。如果线索数字是0bool()函数将返回False,那其他所有为True的元素,就是我们想要的。

我们使用filter()对刚刚编写的函数来缩小字典范围。回想一下上一节,我们需要使用locations.items()来获取键和值作为一对。

for place, clues in filter(has_clues, locations.items()):
    print(place)

请注意,我们不包括has_clues 后面的括号。我们将实际函数作为对象传递!filter将进行实际调用。

果然,运行该代码会打印出我们有线索的五个地点(值 > 0)......

Underground City
Humor Hall of Fame
Lachine Canal
Montreal Jazz Festival
St. Lawrence River

在本系列的后面部分,我们将学习 lambdas匿名函数,我们不用单独定义函数。代码如下......

for place, clues in filter(lambda x:bool(x[1]), locations.items()):
    print(place)

map

map()函数的方式与 类似filter(),只是它不是使用函数从可迭代对象中省略元素,而是用于更改它们。

假设我们有一个华氏温度列表:

temps = [67.0, 72.5, 71.3, 78.4, 62.1, 80.6]

我们想将这些都转换为摄氏度,所以我们为此编写了一个函数。

def f_to_c(temp):
    return round((temp - 32) / 1.8, 1)

我们可以使用该map()函数将其应用于temps 中的每个值,从而生成可以在循环(或任何地方)中使用的迭代器。

for c in map(f_to_c, temps):
    print(f&#39;{c}°C&#39;)

请记住,我们将函数对象 f_to_c作为map() 的第一个参数传递,所以我们去掉括号!

运行该循环输出:

19.4°C
22.5°C
21.8°C
25.8°C
16.7°C
27.0°C

min和max

让我们继续使用刚刚的温度列表例子。如果我们想在列表中找到最低最高,我们可以分别使用min()ormax()函数。这没什么新奇的。

temps = [67.0, 72.5, 71.3, 78.4, 62.1, 80.6]
print(min(temps))
>>> 62.1
print(max(temps))
>>> 80.6

注意:与可迭代对象无关,你还可以使用这些函数来查找你提供给它的参数列表中的最小或最大参数,例如min(4, 5, 6, 7, 8),这将返回4

sorted

通常,你需要对可迭代对象进行排序。Python 通过sorted()内置函数非常有效地做到了这一点。

temps = [67.0, 72.5, 71.3, 78.4, 62.1, 80.6]
for t in sorted(temps):
    print(t)

输出如下...

62.1
67.0
71.3
72.5
78.4
80.6

reversed

大多数时候,扩展索引符号[::-1]将允许你反转列表或其他有序迭代。但如果这不是唯一办法,你也可以使用reversed()函数来反转。

例如,我用sorted()与刚才的函数结合起来......

temps = [67.0, 72.5, 71.3, 78.4, 62.1, 80.6]
for t in reversed(sorted(temps)):
    print(t)

程序输出如下...

80.6
78.4
72.5
71.3
67.0
62.1

sum

另一个快速的内置函数是sum(),它将迭代中的所有元素添加在一起。当然,这只有在所有元素可以添加在一起时才有效。

这样做的一种用途是更早地找到这些温度的平均值。你可能还记得该len()函数告诉我们容器中有多少元素。

temps = [67.0, 72.5, 71.3, 78.4, 62.1, 80.6]
average = sum(temps) / len(temps)
print(round(average, 2))
>>> 71.98

zip

还记得前面那个关于地点和线索的例子吗?想象一下,我们得到的信息不是在字典中,而是在两个列表中:

locations = [&#39;Parade Ground&#39;, &#39;Ste.-Catherine Street&#39;, &#39;Pont Victoria&#39;, &#39;Underground City&#39;, &#39;Mont Royal Park&#39;, &#39;Fine Arts Museum&#39;, &#39;Humor Hall of Fame&#39;, &#39;Lachine Canal&#39;, &#39;Montreal Jazz Festival&#39;, &#39;Olympic Stadium&#39;, &#39;St. Lawrence River&#39;, &#39;Old Montréal&#39;, &#39;McGill University&#39;, &#39;Chalet Lookout&#39;, &#39;Île Notre-Dame&#39;]
clues = [0, 0, 0, 3, 0, 0, 2, 4, 1, 0, 2, 0, 0, 0, 0]

那么刚刚的方法就不能实现查找了,但是在现实世界中确实存在我们会以这种方式获取数据的场景。

值得庆幸的是,zip()函数可以帮助我们处理这些数据,方法是使用迭代器将其聚合成元组,给我们(locations[0], clues[0]), (locations[1], clues[1]), (locations[2], clues[2])等等。

zip()函数甚至不限于两个迭代;它可以尽可能多地压缩在一起!如果迭代的长度不同,“额外”内容会被放到最后。

当然,在这种情况下,两个列表的长度相同,所以结果是相当明显的。让我们使用 zip 中的数据创建一个新列表,并将其打印出来。

data = list(zip(locations, clues))
print(data)

这给了我们一个与我们.items()之前从字典函数中得到的结构不同的结构!

[(&#39;Parade Ground&#39;, 0), (&#39;Ste.-Catherine Street&#39;, 0), (&#39;Pont Victoria&#39;, 0), (&#39;Underground City&#39;, 3), (&#39;Mont Royal Park&#39;, 0), (&#39;Fine Arts Museum&#39;, 0), (&#39;Humor Hall of Fame&#39;, 2), (&#39;Lachine Canal&#39;, 4), (&#39;Montreal Jazz Festival&#39;, 1), (&#39;Olympic Stadium&#39;, 0), (&#39;St. Lawrence River&#39;, 2), (&#39;Old Montréal&#39;, 0), (&#39;McGill University&#39;, 0), (&#39;Chalet Lookout&#39;, 0), (&#39;Île Notre-Dame&#39;, 0)]

事实上,如果我回忆起我filter()的 lambda 函数,我可以将它调整为 use zip,让我们完全从两个列表中工作:

for place, clues in filter(lambda x:bool(x[1]), zip(locations, clues)):
    print(place)

和以前一样,输出...

Underground City
Humor Hall of Fame
Lachine Canal
Montreal Jazz Festival
St. Lawrence River

itertools

我已经介绍了几乎所有 Python 用于处理可迭代对象的内置函数,但itertools模块中还有更多内容。我强烈建议阅读官方文档以了解更多信息。

以上就是Python迭代工具有哪些及怎么使用的详细内容,更多请关注其它相关文章!