如何将元组列表转换为多个列表?

153

假设我有一个元组列表,我想将其转换为多个列表。

例如,元组列表为

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

Python中是否有任何内置函数可以将其转换为:

[1,3,5],[2,4,6]

这可以是一个简单的程序。但我只是好奇Python中是否存在这样的内置函数。

7个回答

222

内置函数zip()几乎可以实现你想要的功能:

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

唯一的区别是你会得到元组而不是列表。你可以使用以下方式将它们转换为列表

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

在Python中,您需要将生成的迭代器转换为列表才能查看内容:list(zip(*[(1, 2), (3, 4), (5, 6)])) - MERose
1
@MERose 这个答案是针对Python 2编写的。在当时,zip()map()返回的是列表,而不是像在Python 3中一样的惰性迭代器。我会更新这个答案。 - Sven Marnach
我知道,我知道。我的评论是为了避免今天的观众产生困惑。另外,我忘记写“Python 3”了。 - MERose

55

来自Python文档:

使用zip()和*操作符可以解压一个列表:

具体示例:

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

或者,如果你真的想要列表:

>>> map(list, zip(*[(1, 2), (3, 4), (5, 6)]))
[[1, 3, 5], [2, 4, 6]]

11
>>> a = [(1,2),(3,4),(5,6),]    
>>> zip(*a)
[(1, 3, 5), (2, 4, 6)]

7

franklsf95在他的回答中追求性能并选择了list.append(),但它们并不是最优的。

通过添加列表推导式,我最终得到了以下代码:

def t1(zs):
    xs, ys = zip(*zs)
    return xs, ys

def t2(zs):
    xs, ys = [], []
    for x, y in zs:
        xs.append(x)
        ys.append(y)
    return xs, ys

def t3(zs):
    xs, ys = [x for x, y in zs], [y for x, y in zs]
    return xs, ys

if __name__ == '__main__':
    from timeit import timeit
    setup_string='''\
N = 2000000
xs = list(range(1, N))
ys = list(range(N+1, N*2))
zs = list(zip(xs, ys))
from __main__ import t1, t2, t3
'''
    print(f'zip:\t\t{timeit('t1(zs)', setup=setup_string, number=1000)}')
    print(f'append:\t\t{timeit('t2(zs)', setup=setup_string, number=1000)}')
    print(f'list comp:\t{timeit('t3(zs)', setup=setup_string, number=1000)}')

这产生了以下结果:
zip:            122.11585397789766
append:         356.44876132614047
list comp:      144.637765085659

如果您追求性能,您应该使用zip(),虽然列表推导式的性能也不差。相比之下,append的性能实际上非常差。

7
尽管使用 *zip 更符合 Python 风格,但以下代码具有更好的性能:
xs, ys = [], []
for x, y in zs:
    xs.append(x)
    ys.append(y)

此外,当原始列表 zs 为空时,*zip 会引发错误,但此代码可以正确处理。
我刚刚进行了一个快速实验,以下是结果:
Using *zip:     1.54701614s
Using append:   0.52687597s

运行多次后,appendzip 快 3 倍到 4 倍!测试脚本在这里:

#!/usr/bin/env python3
import time

N = 2000000
xs = list(range(1, N))
ys = list(range(N+1, N*2))
zs = list(zip(xs, ys))

t1 = time.time()

xs_, ys_ = zip(*zs)
print(len(xs_), len(ys_))

t2 = time.time()

xs_, ys_ = [], []
for x, y in zs:
    xs_.append(x)
    ys_.append(y)
print(len(xs_), len(ys_))

t3 = time.time()

print('Using *zip:\t{:.8f}s'.format(t2 - t1))
print('Using append:\t{:.8f}s'.format(t3 - t2))

我的Python版本:

Python 3.6.3 (default, Oct 24 2017, 12:18:40)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

3
除了 Claudiu 的答案之外,您还可以使用:
>>>a, b = map(list, zip(*[(1, 2), (3, 4), (5, 6)]))
>>>a
[1,3,5]
>>>b
[2,4,6]

根据 @Peyman mohseni kiasari 的修改进行了编辑。

1
不!它会给你(1, 3, 5)(2, 4, 6)而不是列表。你应该使用map(list, zip(*[(1, 2), (3, 4), (5, 6)])) - Peyman

0

在补充Claudiu和Claudiu的答案之后,由于在Python 3中需要从itertools导入map,您还可以使用列表推导式,例如:

[[*x] for x in zip(*[(1,2),(3,4),(5,6)])]
>>> [[1, 3, 5], [2, 4, 6]]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接