Python中zip的一些内容

1、zip基本说明

zip函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,返回由这些元组组成的列表。在Python3.x中为了减少内存,返回的是一个对象,可以用list()转换来输出列表。

如果各个迭代器的元素个数不一样,则返回列表长度与最短的对象相同,利用*号操作符,可以将元组解压为列表。

语法

1
zip([iterable, ...])
  • 参数:
    • iterable:一个或多个迭代器;
  • 返回值:
    • 返回一个对象;

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
a = [1,2,3]
b = [4,5,6]
c = [4,5,6,7,8]

zipped = zip(a,b) # 返回一个对象
zipped
# <zip object at 0x103abc288>
list(zipped) # list() 转换为列表
# [(1, 4), (2, 5), (3, 6)]
list(zip(a,c)) # 元素个数与最短的列表一致
# [(1, 4), (2, 5), (3, 6)]

a1, a2 = zip(*zip(a,b)) # 与 zip 相反,zip(*) 可理解为解压,返回二维矩阵式,*是Python函数可变参数的一种表示形式,加*的表示传入一个元组对象进行解包
list(a1)
# [1, 2, 3]
list(a2)
# [4, 5, 6]

2、一些使用

相邻元素压缩器

1
2
3
4
5
a = [1, 2, 3, 4, 5, 6]
list(zip(*([iter(a)] * 2)))
# [(1, 2), (3, 4), (5, 6)]
list(zip(*([iter(a)] * 3)))
# [(1, 2, 3), (4, 5, 6)]
  • 首先理解迭代器,iter()可以将一个序列生成为一个迭代器,迭代器的特点是可以用for in语句迭代。原理是迭代器对象有一个next方法,可以每次移动迭代的指针,一旦迭代完,没有下一个元素的时候,会触发一个StopIteration异常。迭代器的特点是,迭代了一次以后,指针就移动了,不会自动回溯。例如,可以用for in迭代列表无数次,但只能迭代一次迭代器,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    >>> a = [1, 2, 3, 4, 5, 6]
    >>> x = iter(a)
    >>> for i in x:
    ... print(i)
    ...
    1
    2
    3
    4
    5
    6
    >>> for i in x:
    ... print(i) # 因为x已经被迭代过了,迭代的指针不会回溯,所以没有值了
    ...
    >>>

    而在上述相邻元素压缩器的使用过程中,可以发现:

    1
    2
    3
    4
    5
    a = [1, 2, 3, 4, 5, 6]
    [iter(a)] * 3
    # [<listiterator at 0x7fa6c00ec1d0>,
    # <listiterator at 0x7fa6c00ec1d0>,
    # <listiterator at 0x7fa6c00ec1d0>]

    三个迭代器实际上是同一个迭代器。

  • 再来理解zip,如前文所述,zip将两个序列对应打包,如:

    1
    2
    3
    4
    >>> a1 = [1, 3, 5]
    >>> a2 = [2, 4, 6]
    >>> list(zip(a1, a2))
    [(1, 2), (3, 4), (5, 6)]

    *则表示传入一个对象进行解包,如:

    1
    2
    3
    4
    5
    >>> t = (a1, a2)
    >>> list(zip(t)) # 不加*号,zip 只有一个参数 t
    [([1, 3, 5],), ([2, 4, 6],)]
    >>> list(zip(*t)) # 加*号的作用就是将元祖t,解包成a1,a2为zip的两个函数参数
    [(1, 2), (3, 4), (5, 6)]
  • 最后理解为什么使用迭代器,如下:

    1
    2
    3
    4
    5
    6
    7
    8
    >>> a = [1, 2, 3, 4, 5, 6]
    >>> x = iter(a)
    >>> t = [a, a]
    >>> list(zip(*t)) # case 1,不使用迭代器
    [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)]
    >>> tx = [x, x]
    >>> list(zip(*tx)) # case 2,使用迭代器
    [(1, 2), (3, 4), (5, 6)]

    在case 1中,zip传入的两个参数a,等于打包了两个列表,并在zip(*)中进行解包,等价于:

    1
    zip([1, 2, 3, 4, 5, 6],[1, 2, 3, 4, 5, 6])

    在case 2中,x是迭代器对象,迭代过程会调用next方法,迭代过一次后会自动移动且不会回溯。也就是说zip执行过程中先调用第一个参数xnext方法得到参数1,再调用第二个参数xnext方法,上文我们已知这两个迭代器其实是同一个迭代器对象,所以第二次调用xnext方法时,迭代器指针已经移动,所以得到参数2,整个过程类似如下:

    1
    2
    3
    4
    5
    6
    x.next -> 1
    x.next -> 2
    zip(x.next(), x.next()) ---> zip(1, 2)
    x.next -> 3
    x.next -> 4
    zip(x.next(), x.next()) ---> zip(3, 4)

    等价于:

    1
    zip([1, 3, 5], [2, 4, 6])

基于此,就可以理解使用zip做相邻元素压缩器的完整执行过程了。

列表元素依次相连

注意与相邻元素压缩器的效果区别。

1
2
3
4
5
temp = ['a', 'b', 'c', 'd', 'e','f']
print(temp)
# ['a', 'b', 'c', 'd', 'e', 'f']
list(zip(temp[:-1],temp[1:]))
# [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e'), ('e', 'f')]

这个用法比较好理解,temp[:-1]去除列表最后一个元素,temp[1:]去除列表第一个元素,则上述过程等价于:

1
zip(['a', 'b', 'c', 'd', 'e'], ['b', 'c', 'd', 'e', 'f'])

基于该用法可衍生出多个元素依次相连的用法,如:

1
2
list(zip(temp[:-2],temp[1:-1],temp[2:]))
# [('a', 'b', 'c'), ('b', 'c', 'd'), ('c', 'd', 'e'), ('d', 'e', 'f')]

也可以用这种方法实现列表相邻元素压缩器的效果,如:

1
2
3
a = [1, 2, 3, 4, 5, 6]
list(zip(a[::3], a[1::3], a[2::3]))
# [(1, 2, 3), (4, 5, 6)]

取列表相同位置元素

1
2
3
4
5
6
7
8
nums = ['flower','flow','flight']
for i in zip(*nums):
print(i)

# ('f', 'f', 'f')
# ('l', 'l', 'l')
# ('o', 'o', 'i')
# ('w', 'w', 'g')

反转字典

1
2
3
m = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
dict(zip(m.values(), m.keys()))
# {1: 'a', 2: 'b', 3: 'c', 4: 'd'}
-------------本文结束感谢您的阅读-------------
0%