这两个函数不是仅供循环语句使用,但它们可以帮助循环语句更容易实现某些功能。
1. zip()
Python 内置函数 zip()
的基本调用形式是 zip(*iterables)
,其参数应为可迭代对象,且用符号 *
表示可以是多个可迭代对象(参阅第7章7.2节),例如:
>>> zip("abc", "def")
<zip object at 0x7f8293d44f40>
函数的返回值是一个迭代器对象( zip object
。关于迭代器,请参阅第9章9.6节),该对象的成员是由参数中的可迭代对象的成员依次对应地组成的元组。可以用 list()
函数将其转化为列表,从而显示迭代器对象的内部成员。
>>> list(zip("abc", "def"))
[('a', 'd'), ('b', 'e'), ('c', 'f')]
由此可见,zip()
的作用就是将可迭代对象的成员进行“配对”,即:将可迭代对象中的成员相对应地建立映射关系。
如果长度不同,则“以短的为准”(帮助文档中表述为“This continues until the shortest argument is exhausted”)。
>>> a = ['python', 'ml']
>>> list(zip(a, range(4), {'name':'laoqi', 'age':30, 'book':'python'})) # (9)
[('python', 0, 'name'), ('ml', 1, 'age')]
注释(9)中以包含不同成员数量的不同类型的可迭代对象为 zip()
的参数,请认真观察返回结果。
理解了基本应用方法之后,用它来解决这样一个问题:分别有数列 a = range(1, 6)
和 b = range(9, 4, -1)
,计算两个数列中对应项的乘积。
>>> a = range(1, 6)
>>> b = range(9, 4, -1)
>>> c = []
>>> for x, y in zip(a, b): # (10)
... c.append(x * y)
...
>>> c
[9, 16, 21, 24, 25]
注释(10)中以 zip(a, b)
得到 a
和 b
的对应项的“配对组合”,经循环语句之后,逐个求积,并追加到前面已经创建的空列表 c
中——在6.4节会对这种写法进行优化。
仿照上面的方法,再做一个练习:将字典 myinfor = {"publish":"phei", "site":"itdiffer.com", "lang":"python"}
变换成 infor = {"phei":"publish", "itdiffer.com":"site", "python":"lang"}
,即原字典的键值对中的键、值位置互换。
# 方法 1
>>> myinfor = {"publish":"phei", "site":"itdiffer.com", "lang":"python"}
>>> infor = dict()
>>> for k, v in myinfor.items():
... infor[v] = k
...
>>> infor
{'phei': 'publish', 'itdiffer.com': 'site', 'python': 'lang'}
# 方法 2
>>> dict(zip(myinfor.values(), myinfor.keys()))
{'phei': 'publish', 'itdiffer.com': 'site', 'python': 'lang'}
是否能理解方法2?如果有疑问,请参考 dict()
函数的使用方法,并结合 zip()
函数返回结果。除了上述两个方法之外,6.4节还会提供第三种实现。
2. enumerate()
在学习使用这个函数之前,先做个练习——还是强调“温故而知新”的学习方法:使用标准库中的 random 模块,生成一个含有 20 个成员的列表,其成员是由 1 到 10 中随机整数组成,然后将此列表中的偶数用字符串 even
替换。
暂时不要看下面的代码,请读者自己尝试,是否能完成这个练习。
#coding:utf-8
'''
filename: even.py
'''
import random
lst = []
for i in range(20):
lst.append(random.randint(1, 10)) # (11)
print(lst)
for n in lst:
if n % 2 == 0:
idx = lst.index(n) # (12)
lst[idx] = 'even'
print(lst)
先看执行结果,再解释部分代码。
% python even.py
[3, 10, 9, 4, 2, 2, 4, 6, 1, 3, 3, 10, 1, 4, 4, 2, 9, 4, 1, 5]
[3, 'even', 9, 'even', 'even', 'even', 'even', 'even', 1, 3, 3, 'even', 1, 'even', 'even', 'even', 9, 'even', 1, 5]
从执行结果上看,上述代码已经解决了练习中的问题。看注释(11),random.randint(1, 10)
可以生成一个随机数——本质上是伪随机,并将该随机数追加到列表中(6.4节会有更优化的方法实现此操作)。再看注释(12),通过条件语句判断某个数是不是偶数,如果是,则得到其在列表 lst
中的索引,然后用 lst[idx]
将该索引位置对应的对象更换为字符串 'even'
。
在理解了上面的程序之后,再介绍的函数 enumerae()
,并将其用于优化上述程序,从而体验此函数的作用。
>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> enumerate(seasons) # (13)
<enumerate object at 0x7f82960069c0>
>>> list(enumerate(seasons)) # (14)
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
函数 enumerate()
的返回值是可迭代的 enumerate 对象(如注释(13)的返回对象),依据学习 zip()
的经验,按照注释(14)那样,用 list()
函数将其转化为列表,就能看到此对象里面的成员,是由原列表 seasons
的索引及其对应成员构成的元组——表示二者的映射关系。下面在循环语句中使用它:
>>> for i, item in enumerate(seasons):
... print(f"index: {i}, item: {item}")
...
index: 0, item: Spring
index: 1, item: Summer
index: 2, item: Fall
index: 3, item: Winter
用这个方法,优化注释(12),该程序可以修改为:
#coding:utf-8
'''
filename: even.py
'''
import random
lst = []
for i in range(20):
lst.append(random.randint(1, 10))
print(lst)
# for n in lst:
for idx, n in enumerate(lst):
if n % 2 == 0:
# idx = lst.index(n)
lst[idx] = 'even'
print(lst)
最后,建议读者认真阅读 enumerate()
函数的文档,将操作结果与文档叙述进行对照。
★自学建议 第4章4.4节的【自学建议】中曾建议读者“结对学习”,“独学而无友,则孤陋而寡闻”(《礼记·学记》),除了与周边人结对学习外,还可以加入学习讨论群。一般讨论群是基于社交软件建立的,有意愿学习的同道中人聚集在一起,一来可以通过观看其他人的讨论,了解大家的学习进度和遇到的问题,并反思自己是否能“有则改之无则加勉”;二来可以跟“同学”进行讨论,帮助“同学”解决问题,既能助人为乐,又能提升自己的水平。 最后要提醒诸位读者,不论是提问还是回答问题,都要注意“语言美”,《会说话的人运气都不会太差》((日)矢野香 著,王军 译)。 ”