>>> from env_helper import info; info()
页面更新时间: 2020-11-19 21:00:42
操作系统/OS: Linux-4.19.0-11-amd64-x86_64-with-debian-10.6 ;Python: 3.7.3

2.8. while 循环

为避免前述示例所示的繁琐代码,能够像下面这样做很有帮助:

>>> x = 1
>>> while x <= 100:
>>>     print(x, end = ',')
>>>     x += 1
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,

利用 while 语句,可以让一个代码块一遍又一遍的执行。 只要 while语句的条件为 Truewhile 子句中的代码就会执行。 在代码中, while 语句总是包含下面几部分:

  • 关键字;

  • 条件(求值为 TrueFalse 的表达式);

  • 冒号;

  • 从新行开始,缩进的代码块(称为 while 子句)。

那么如何使用Python来实现的?你猜对了,就像上面那样做。不太复杂,不是吗?你还可以 使用循环来确保用户输入名字,如下所示:

>>> name = ''
>>> while not name:
>>>     name = input('Please enter your name:  ')
>>> print('Hello, {}!'.format(name))
Please enter your name:
Please enter your name:  Tome
Hello, Tome!

请尝试运行这些代码,并在要求你输入名字时直接按回车键。你会看到提示信息再次出现, 因为name还是为空字符串,这相当于假。

提示 如果你只是输入一个空格字符(将其作为你的名字),结果将如何呢?试试看。程序将 接受这个名字,因为包含一个空格字符的字符串不是空的,因此不会将name视为假。这 无疑是这个小程序的一个瑕疵,但很容易修复:只需将while not name改为while not name or name.isspace()或while not name.strip()即可。

while语句非常灵活,可用于在条件为真时反复执行代码块。这在通常情况下很好,但有时 候你可能想根据需要进行定制。一种这样的需求是为序列(或其他可迭代对象)中每个元素执行 代码块。

注意 基本上,可迭代对象是可使用for循环进行遍历的对象。第9章将详细介绍可迭代对象和 迭代器。就目前而言,只需将可迭代对象视为序列即可。

为此,可使用for语句:

>>> words = ['this', 'is', 'an', 'ex', 'parrot']
>>> for word in words:
>>>     print(word, end = ' | ')
this | is | an | ex | parrot |

>>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for number in numbers:
>>>     print(number, end=',')
0,1,2,3,4,5,6,7,8,9,

鉴于迭代(也就是遍历)特定范围内的数是一种常见的任务,Python提供了一个创建范围的内置函数。

>>> range(0, 10)
range(0, 10)
>>> list(range(0, 10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

范围类似于切片。它们包含起始位置(这里为0),但不包含结束位置(这里为10)。在很多 情况下,你都希望范围的起始位置为0。实际上,如果只提供了一个位置,将把这个位置视为结 束位置,并假定起始位置为0。

>>> range(10)
range(0, 10)

下面的程序打印数1~100:

>>> for number in range(1,101):
>>>     print(number, end = ',')
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,

注意,相比前面使用的while循环,这些代码要紧凑得多。

提示 只要能够使用 for 循环,就不要使用 while 循环。

2.8.1. 迭代字典

要遍历字典的所有关键字,可像遍历序列那样使用普通的for语句。

>>> d = {'x': 1, 'y': 2, 'z': 3}
>>> for key in d:
>>>     print(key, 'corresponds to', d[key])
x corresponds to 1
y corresponds to 2
z corresponds to 3

也可使用keys等字典方法来获取所有的键。如果只对值感兴趣,可使用d.values。你可能还记得,d.items以元组的方式返回键-值对。for循环的优点之一是,可在其中使用序列解包。

>>> for key, value in d.items():
>>>     print(key, 'corresponds to', value)
x corresponds to 1
y corresponds to 2
z corresponds to 3

注意 字典元素的排列顺序是不确定的。换而言之,迭代字典的键或值时,一定会处理所有的 键或值,但不知道处理的顺序。如果顺序很重要,可将键或值存储在一个列表中并对列 表排序,再进行迭代。要让映射记住其项的插入顺序,可使用模块collections中的 OrderedDict类。

2.8.2. 一些迭代工具

Python提供了多个可帮助迭代序列(或其他可迭代对象)的函数,其中一些位于第10章将介 绍的模块itertools中,但还有一些内置函数使用起来也很方便。

并行迭代

有时候,你可能想同时迭代两个序列。假设有下面两个列表:

>>> names = ['anne', 'beth', 'george', 'damon']
>>> ages = [12, 45, 32, 102]

如果要打印名字和对应的年龄,可以像下面这样做:

>>> for i in range(len(names)):
>>>     print(names[i], 'is', ages[i], 'years old')
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old

i是用作循环索引的变量的标准名称。

一个很有用的并行迭代工具是内置函数 zip ,它将两个序列“缝合”起来,并返回一个由元组组成的序列。返回值是一个适合迭代的对象,要查看其内容,可使用 list() 将其转换为列表。

>>> list(zip(names, ages))
[('anne', 12), ('beth', 45), ('george', 32), ('damon', 102)]

“缝合”后,可在循环中将元组解包。

>>> for name, age in zip(names, ages):
>>>     print(name, 'is', age, 'years old')
anne is 12 years old
beth is 45 years old
george is 32 years old
damon is 102 years old

函数zip可用于“缝合”任意数量的序列。需要指出的是,当序列的长度不同时,函数zip将 在最短的序列用完后停止“缝合”。

>>> list(zip(range(5), range(100000000)))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]

迭代时获取索引

在有些情况下,你需要在迭代对象序列的同时获取当前对象的索引。例如,你可能想替换一 个字符串列表中所有包含子串’xxx’的字符串。当然,完成这种任务的方法有很多,但这里假设 你要像下面这样做:

for string in strings:
    if 'xxx' in string:
        index = strings.index(string) # 在字符串列表中查找字符串
        strings[index] = '[censored]'

这可行,但替换前的搜索好像没有必要。另外,如果没有替换,搜索返回的索引可能不对(即 返回的是该字符串首次出现处的索引)。下面是一种更佳的解决方案:

index = 0
for string in strings:
    if 'xxx' in string:
    strings[index] = '[censored]'
    index += 1

这个解决方案虽然可以接受,但看起来也有点笨拙。另一种解决方案是使用内置函数enumerate。

for index, string in enumerate(strings):
    if 'xxx' in string:
        strings[index] = '[censored]'

这个函数让你能够迭代索引-值对,其中的索引是自动提供的。

反向迭代和排序后再迭代

来看另外两个很有用的函数: reversed()sorted() 。 它们类似于列表方法 reverse()sort()sorted() 接受的参数也与 sort() 类似), 但可用于任何序列或可迭代的对象,且不就地修改对象,而是返回反转和排序后的版本。

>>> sorted([4, 3, 6, 8, 3])
[3, 3, 4, 6, 8]
>>> sorted('Hello, world!')
[' ', '!', ',', 'H', 'd', 'e', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
>>> list(reversed('Hello, world!'))
['!', 'd', 'l', 'r', 'o', 'w', ' ', ',', 'o', 'l', 'l', 'e', 'H']
>>> ''.join(reversed('Hello, world!'))
'!dlrow ,olleH'

请注意,sorted返回一个列表,而reversed像zip那样返回一个更神秘的可迭代对象。你无需 关心这到底意味着什么,只管在for循环或join等方法中使用它,不会有任何问题。只是你不能 对它执行索引或切片操作,也不能直接对它调用列表的方法。要执行这些操作,可先使用list对 返回的对象进行转换。

提示 要按字母表排序,可先转换为小写。为此,可将sort或sorted的key参数设置为str.lower。 例如,sorted(“aBc”, key=str.lower)返回[‘a’, ‘B’, ‘c’]。