引子
最近在刷题时碰到一个排序的子问题,问题大致是有两个整型数组a和b,a和b的元素是一一对应的,现需要以a为基准排序,且a和b的对应关系不能变。
假设有数组
a
=
[
100
,
700
,
300
,
150
,
450
]
a = [100, 700, 300, 150, 450]
a=[100,700,300,150,450]和
b
=
[
1
,
2
,
3
,
4
,
5
]
b = [1, 2, 3, 4, 5]
b=[1,2,3,4,5],根据上述题目要求,排序后应当有
a
=
[
100
,
150
,
300
,
450
,
700
]
a = [100, 150, 300, 450, 700]
a=[100,150,300,450,700]和
b
=
[
1
,
4
,
3
,
5
,
2
]
b = [1, 4, 3, 5, 2]
b=[1,4,3,5,2]。这个问题可以很方便的用Python的内置函数zip()和sorted()进行解决,代码如下所示:
a = [100, 700, 300, 150, 450]
b = [1, 2, 3, 4, 5]
z1, z2 = zip(*sorted(zip(a, b)))
z1 = list(z1)
z2 = list(z2)
print('a =', z1, '\nb =', z2)
输出为:
a = [100, 150, 300, 450, 700]
b = [1, 4, 3, 5, 2]
说明
zip()函数用于将可迭代的对象(列表、元组、字典)作为参数,将对象中对应的元素打包成一个个元素,然后返回由这些元素组成的对象(Python 3中返回对象,Python 2中返回列表),这样做的好处是节约了不少的内存。
我们可以使用list()转换来输出列表。
语法
zip语法:
zip([iterable, …])
其中iterable表示一个或多个可迭代对象,在Python里可以是列表、元组、字典。
返回值是一个迭代器对象。
示例
看下面这段代码:
a = [1, 2, 3]
b = [4, 5, 6]
print(zip(a, b))
print(list(zip(a, b)))
输出为:
<zip object at 0x0000015C93E522C8>
[(1, 4), (2, 5), (3, 6)]
zip()函数返回的是可迭代对象,我们想要直接获取它的返回值,一般是将其转换为list或者tuple,而它们内部每个元素都是tuple形式的。
对于返回的list或tuple其内部每个元素都是tuple形式,我们还可以参考下面的例子:
a = [[1, 2], [3, 4]]
b = [[5, 6], [7, 8]]
z = list(zip(a, b))
print(z)
此时的输出为:
[([1, 2], [5, 6]), ([3, 4], [7, 8])]
当在循环中使用zip()函数时,实际上是使用的zip对象的__next__()方法。
比如我们可以这样用:
for z1, z2 in zip(a, b):
print(z1, z2)
输出就为:
1 4
2 5
3 6
或者我们可以直接调用__next__()方法:
z = zip(a, b)
for i in range(len(a)):
print(z.__next__())
这样输出就为:
(1, 4)
(2, 5)
(3, 6)
由于zip()函数返回的是迭代器对象,所以我们不能反复的使用返回值,比如像下面这样:
z = zip(a, b)
print(list(z))
for z1, z2 in z:
print(z1, z2)
这时的输出是:
[(1, 4), (2, 5), (3, 6)]
可以看到for循环中无法再次获取值了。其实,这个问题也是可以解决的,只要将返回的对象转换成list或者tuple保存到新的变量就可以了,如下所示:
z = list(zip(a, b))
print(z)
for z1, z2 in z:
print(z1, z2)
这时的输出是:
[(1, 4), (2, 5), (3, 6)]
1 4
2 5
3 6
另外,如果zip()函数输入的各个迭代器的元素个数不一致,则返回的列表与最短长度的对象相同,比如:
a = [1, 2, 3]
b = [4, 5, 6, 7, 8]
z = list(zip(a, b))
print(z)
输出为:
[(1, 4), (2, 5), (3, 6)]
最后,利用*
操作符,也就是zip(*)函数可以将元组解压为列表,比如:
c = [['A', 'a'], ('B', 'b'), ('C', 'c')]
print(list(zip(*c)))
输出为:
[('A', 'B', 'C'), ('a', 'b', 'c')]
也就是说上面的c可以看做('A', 'B', 'C')
和('a','b', 'c')
压缩而成,解压后自然就得到他们两个了。
现在,对于引子里面的例子我想你应该看明白了。首先我们调用zip()函数将a和b打包成元组,然后再使用sorted()函数对其进行排序。由于排序的每个元素是一个由a和b中的对应元素组成的元组,而元组排序时默认以第一个成员为基准进行排序(也就是a中的元素),排序好之后再使用zip(*)函数对其解压,那么就得到了以a中元素为基准排序且b中元素与之一一对应的两个元组了。最后再将其转换成列表就是我们所要的了(根据实际需要也可以不转换,a和b现在都是元组类型,也是可迭代的)。
如果文章中有什么问题,欢迎读者朋友们在评论区指出,我会及时纠正。