[TOC]

zip-map-enumerate

  • 先重温一下迭代(Iteration)、迭代器对象(iterable)、迭代器(iterator )的概念:

  • Iteration是计算机科学的通用术语,它是指对一组元素执行一项操作,一次执行一个元素。一个很好的例子是循环 - 它适用于每个单独的项目,直到整个项目集运行完毕为止。

  • Iterable是可以遍历的对象(译者注:在Python中所有东西都是object, 比如说变量,容器,类),iterable是可以产生iterator的object。

  • iterator是表示数据流的对象,它一次返回一个元素的数据。它还会记住其在迭代过程中的位置。本质上,它控制应如何迭代可迭代对象。

zip()的作用

先看一下语法:

zip(iter1 [,iter2 [...]]) —> zip object
Python的内置help()模块提供了一个简短但又有些令人困惑的解释:

返回一个元组迭代器,其中第i个元组包含每个参数序列或可迭代对象中的第i个元素。当最短的可迭代输入耗尽时,迭代器将停止。使用单个可迭代参数,它将返回1元组的迭代器。没有参数,它将返回一个空的迭代器。

与往常一样,当您精通更一般的计算机科学和Python概念时,此模块非常有用。但是,对于初学者来说,这段话只会引发更多问题。让我们尝试通过示例,代码片段和可视化来解释zip()功能:从许多迭代中获取元素,然后…… 放在一起

我们可以通过几个列表来演示zip()的功能:

1
2
3
4
5
uppercase = ['A', 'B', 'C']
lowercase = ['a', 'b', 'c']

for x, y in zip(uppercase, lowercase):
print(x, y)
A a
B b
C c

但是,不限于两个可迭代对象作为参数传递-我们可以添加任意多个:

1
2
3
4
5
6
uppercase = ['A', 'B', 'C']
lowercase = ['a', 'b', 'c']
numbers = [1, 2, 3]

for x, y, z in zip(uppercase, lowercase, numbers):
print(x, y, z)
A a 1
B b 2
C c 3

zip()函数的另一个重要警告是如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同:

1
2
3
4
5
6
uppercase = ['A', 'B', 'C', 'D', 'E']
lowercase = ['a', 'b', 'c', 'd']
numbers = [1, 2, 3]

for x, y, z in zip(uppercase, lowercase, numbers):
print(x, y, z)
A a 1
B b 2
C c 3

正如我们所看到的,即使列出了三个三元组,uppercase和lowercase列表具有5和4个元素。

要知道的重要一件事是zip()函数返回什么。

尽管似乎在调用此函数时会得到一个列表,但实际上它返回一个称为zip object的特殊数据类型,这意味着使用索引将无法浏览,下面我们学习如何将其转换为其他数据类型(例如列表)。

在此之前,我们还应再来学习一下迭代(Iteration)、迭代器对象(iterable)、迭代器(iterator )的概念:

Iteration是计算机科学的通用术语。它是指对一组元素执行一项操作,一次执行一个元素。一个很好的例子是循环 -它适用于每个单独的项目,直到整个项目集运行完毕为止。
Iterable是可以遍历的对象(译者注:在Python中所有东西都是object, 比如说变量,容器,类),iterable是可以产生iterator的object。
iterator是表示数据流的对象,它一次返回一个元素的数据。它还会记住其在迭代过程中的位置。本质上,它控制应如何迭代可迭代对象。

将zip()对象转换为列表(并使用索引)

zip()函数返回一个zip对象(类似于map()操作方式)。

zip对象提供了一些有趣的功能(迭代速度比list更快),但是我们经常需要将其转换为list。为了做到这一点,我们需要调用list()函数:

1
2
3
4
5

b = ["red", "green", "blue"]
c = ["leopard", "cheetah", "jaguar"]

print(list(zip(b, c)))
[('red', 'leopard'), ('green', 'cheetah'), ('blue', 'jaguar')]
1
2
3
4
5
6
7

b = ["red", "green", "blue"]
c = ["leopard", "cheetah", "jaguar"]
new_list = list(zip(b, c))
print(new_list[0])
print(new_list[1])
print(new_list[2])
('red', 'leopard')
('green', 'cheetah')
('blue', 'jaguar')

将zip()对象转换成字典

另外,该dict()函数可用于将zip对象转换为字典。需要注意的是,只能使用两个zip()参数-前者产生key,后者产生value:

1
2
3
b = ["red", "green", "blue"]
f = ["strawberry", "kiwi", "blueberry"]
print(dict(zip(b, f)))
{'red': 'strawberry', 'green': 'kiwi', 'blue': 'blueberry'}

解压列表

在某些情况下,我们需要执行相反的操作——解压迭代器。解压操作涉及将压缩后的元素恢复为其原始状态。为此,我们添加*运算符到函数调用中。例:

1
2
3
4
5
6
7
a = [1, 2, 3]
b = [4, 5, 6]
zipped = zip(a, b)
list(zipped)

a2, b2 = zip(*zip(a, b))
print(a == list(a2) and b == list(b2))
True

Zip与列表生成式(for循环潜在问题)

zip()函数与Python中的for循环一起使用的可视化

在应用for循环后注意缺少的元素!

Python的另一个很棒的功能——列表推导式,可以与zip()函数结合使用。表面上看起来很简单……

1
2
3
4
m = ["mind", "mouse", "mini"]
n = ["norm", "night", "necklace"]

[print(a, b) for a, b in zip(m, n)]
mind norm
mouse night
mini necklace

[None, None, None]

enumerate()

枚举字符串

字符串只是一个列表

为了更好地理解字符串枚举,我们可以将给定的字符串想象为单个字符(项)的集合。因此,枚举字符串将为我们提供:

1.字符的索引。2.字符的值。

1
2
3
word = "Speed"
for index, char in enumerate(word):
print(f"The index is '{index}' and the character value is '{char}'")
The index is '0' and the character value is 'S'
The index is '1' and the character value is 'p'
The index is '2' and the character value is 'e'
The index is '3' and the character value is 'e'
The index is '4' and the character value is 'd'

列举列表

1
2
3
sports = ['soccer', 'basketball', 't`  ennis']
for index, value in enumerate(sports):
print(f"The item's index is {index} and its value is '{value}'")
The item's index is 0 and its value is 'soccer'
The item's index is 1 and its value is 'basketball'
The item's index is 2 and its value is 't`  ennis'

自定义起始索引

我们可以看到枚举从索引0开始,但是们经常需要更改起始位置,以实现更多的可定制性。值得庆幸的是,enumerate()还带有一个可选参数[start]
enumerate(iterable, start=0)

1
2
3
students = ['John', 'Jane', 'J-Bot 137']  
for index, item in enumerate(students, start=1):
print(f"The index is {index} and the list element is '{item}'")
The index is 1 and the list element is 'John'
The index is 2 and the list element is 'Jane'
The index is 3 and the list element is 'J-Bot 137'
1
2
3
teachers = ['Mary', 'Mark', 'Merlin']
for index, item in enumerate(teachers, -5):
print(f"The index is {index} and the list element is '{item}'")
The index is -5 and the list element is 'Mary'
The index is -4 and the list element is 'Mark'
The index is -3 and the list element is 'Merlin'

枚举元组

1
2
3
colors = ('red', 'green', 'blue')
for index, value in enumerate(colors):
print(f"The item's index is {index} and its value is '{value}'")
The item's index is 0 and its value is 'red'
The item's index is 1 and its value is 'green'
The item's index is 2 and its value is 'blue'

枚举列表中的元组

1
2
3
4
5
letters = [('a', 'A'), ('b', 'B'), ('c', 'C')]
for index, value in enumerate(letters):
lowercase = value[0]
uppercase = value[1]
print(f"Index '{index}' refers to the letters '{lowercase}' and '{uppercase}'")
Index '0' refers to the letters 'a' and 'A'
Index '1' refers to the letters 'b' and 'B'
Index '2' refers to the letters 'c' and 'C'
1
2
3
letters = [('a', 'A'), ('b', 'B'), ('c', 'C')]
for i, (lowercase, uppercase) in enumerate(letters):
print(f"Index '{i}' refers to the letters '{lowercase}' and '{uppercase}'")
Index '0' refers to the letters 'a' and 'A'
Index '1' refers to the letters 'b' and 'B'
Index '2' refers to the letters 'c' and 'C'

枚举字典

枚举字典似乎类似于枚举字符串或列表,但事实并非如此,主要区别在于它们的顺序结构,即特定数据结构中元素的排序方式。

字典有些随意,因为它们的项的顺序是不可预测的。如果我们创建字典并打印它,我们将得到一种结果:

1
2
3
animals = {'cat': 3, 'dog': 6, 'bird': 9}
for key, value in animals.items():
print(key, value)
cat 3
dog 6
bird 9
1
2
3
4
animals = {'cat': 3, 'dog': 6, 'bird': 9}
for i, j in enumerate(animals):
print(i,j)
print(j)
0 cat
cat
1 dog
dog
2 bird
bird

map()的用法

map()函数以迭代的方式将提供的功能应用于每个项目,结果是作为迭代器的map对象。语法:

map(func, *iterables)

如果没有map(),我们将不得不编写复杂的代码以在多个项目上“循环”给定的函数。以一个整洁的小实验为例:我们有一个10个单词的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
test_list = ["effort", "circle", "yearly", "woolen", "accept", "lurker",
"island", "faucet", "glossy", "evader"]

def is_abecedarian(input_word):
index = 0
for letter in input_word[0:-1]:
if ord(input_word[index]) > ord(input_word[index + 1]):
return False
else:
index += 1
return True
# 现在,我们想将函数应用于单词列表,并创建一个将包含True和False值的新列表,以表明某些单词是否确实是abcderian。
# 下面方法涉及初始化一个新列表,然后使用for循环遍历列表元素
value_list = []
for item in test_list:
value = is_abecedarian(item)
value_list.append(value)
print(value_list)
[True, False, False, False, True, False, False, False, True, False]

映射操作(map):一种遍历一个序列并对每个元素执行操作的处理模式。

映射(mapping):一个集合中的每个元素对应另一个集合中的一个元素的关系

将map()转换为列表,元组和集合
由于map()不返回列表/元组/集合,因此我们需要采取额外的步骤来转换生成的map对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def capitalize_word(input_word):
return input_word.capitalize()


map_object = map(capitalize_word, ['strength', 'agility', 'intelligence'])
test_list = list(map_object)
print(test_list)

map_object = map(capitalize_word, ['health', 'mana', 'gold'])
test_set = set(map_object)
print(test_set)

map_object = map(capitalize_word, ['armor', 'weapon', 'spell'])
test_tuple = tuple(map_object)
print(test_tuple)
['Strength', 'Agility', 'Intelligence']
{'Mana', 'Health', 'Gold'}
('Armor', 'Weapon', 'Spell')

将map()与Lambda表达式结合

Lambda表达式是对我们的工具库的一个很好的补充:将Lambda表达式与map()代码相结合可使您的Python程序更小,更精确。

Lambda表达式可以创建匿名函数,即未约定特定标识符的函数。相反,通过def关键字创建函数会将函数绑定到其唯一标识符(例如def my_function创建标识符my_function)。

但是,lambda表达式也有一系列限制:它们每个只能做一件事情,只能在一个地方使用,通常与其他功能结合使用。我们看看lambda表达式如何map()同时使用:

1
2
3
4
5
6
7
8
cities = ["caracas", "bern", "oslo", "ottawa", "bangkok"]


def capitalize_word(input_word):
return input_word.capitalize()


capitalized_cities = map(capitalize_word, cities)

需要注意:map()和lambda表达式提供了凝聚多行代码成一行的能力。尽管此功能非常出色,但我们需要牢记编程的黄金法则之一:代码读取比写入更频繁。这意味着map()和lambda表达式都可以提高代码的简洁性,但是却牺牲了代码的清晰度。遗憾的是,对于代码的可读性,实际上并没有明确的指导方针- 随着编程经验的增长,大家将逐渐明白这一点。

1
2
3
cities = ["caracas", "bern", "oslo", "ottawa", "bangkok"]

capitalized_cities = map(lambda s: s.capitalize(), cities)

使用map()遍历字典

map()也非常适合遍历字典

假设有一个包含苹果,梨和樱桃价格的字典,我们需要通过应用15%的折扣来更新价格表。方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
price_list = {
"pear": 0.60,
"cherries": 0.90,
"apple": 0.35,
}


def calulates_discount(item_price):
return (item_price[0], round(item_price[1] * 0.85, 2))


new_price_list = dict(map(calulates_discount, price_list.items()))

将map()与Lambda表达式组合遍历字典

当开始组合多个功能时,编程特别有趣,一个很好的例子是map()配合使用和lambda表达式来遍历字典。在下面的代码中,我们初始化字典列表,并将每个字典作为参数传递给lambda函数。

1
2
3
4
5
6
7
list_of_ds = [{'user': 'Jane', 'posts': 18}, {'user': 'Amina', 'posts': 64}]

map(lambda x: x['user'], list_of_ds) # Output: ['Jane', 'Amina']

map(lambda x: x['posts'] * 10, list_of_ds) # Output: [180, 640]

map(lambda x: x['user'] == "Jane", list_of_ds) # Output: [True, False]
<map at 0x18242a63808>

map()替代方法:列表解析

像所有技术/产品/方法等等一样,一些Python开发人员认为map()函数在某种程度上不是Python风格(即未遵循应如何构建Python程序的精神和设计理念)。他们建议改用列表解析,比如:

map(f, iterable)
变成

[f(x) for x in iterable]

在速度和性能方面,map()与列表理析大致相等,因此不可能看到执行时间显着减少