在Python中将平面列表转换为列表的列表

23

有时人们希望做的不是将列表扁平化,而是将一个扁平的列表转换为一个嵌套列表,就像这里所描述的:https://dev59.com/qnNA5IYBdhLWcg3wdtld。我想知道如何在Python中将一个扁平的列表转换为一个嵌套列表。

在NumPy中,您可以尝试:

>>> a=numpy.arange(9)
>>> a.reshape(3,3)
>>> a
array([[0, 1, 2],
   [3, 4, 5],
   [6, 7, 8]])

我想知道如何做相反的操作,我的通常解决方案类似于:

>>> Mylist
['a', 'b', 'c', 'd', 'e', 'f']
>>> newList = []
for i in range(0,len(Mylist),2):
...     newList.append(Mylist[i], Mylist[i+1])
>>> newList 
[['a', 'b'], ['c', 'd'], ['e', 'f']]

有没有更符合Python风格的方法来完成这个任务?


最好不要使用list作为变量名,因为有内置函数list() - jamylak
是的,你说得对,我只是快速编辑了代码,我的原始代码不是那样的。 - oz123
4个回答

39
>>> l = ['a', 'b', 'c', 'd', 'e', 'f']
>>> zip(*[iter(l)]*2)
[('a', 'b'), ('c', 'd'), ('e', 'f')]

正如@Lattyware所指出的那样,只有在每个参数中有足够的项时,zip函数才能每次返回一个元组。如果其中一个参数的项数少于其他参数,则会剪切掉一些项。
>>> l = ['a', 'b', 'c', 'd', 'e', 'f','g']
>>> zip(*[iter(l)]*2)
[('a', 'b'), ('c', 'd'), ('e', 'f')]

如果是这种情况,最好使用@Sven Marnach提供的解决方案。 zip(*[iter(s)]*n)是如何工作的?

3
只有当有足够的物品填满它时,这个解决方案才有效,否则物品会被切断。 - Gareth Latty
1
@NickT 嗯,严格来说它是一个迭代器而不是生成器,我也不能为这个聪明想法负责 :) - jamylak
我看到它能工作,但这似乎很可怕和晦涩。依靠zip以这种顺序访问iter(l)的两个副本来产生所需的结果是一个好主意吗? - ToolmakerSteve
2
生成一个列表嵌套的列表:map(list,zip(*[iter(l)]*2)),或 map(list,zip(*[iter(l)]*3)),等等。 - Robert
@jamylak,您能否解释一下为什么这个操作的输出结果不是-- [('a','a'),('b','b').....],因为您正在制作两个迭代器的副本,并且zip将同时从它们两个中获取。 - Azrael
显示剩余3条评论

13

通常可以使用itertools文档中的grouper配方来完成此操作:

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return itertools.izip_longest(fillvalue=fillvalue, *args)

例子:

>>> my_list = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> list(grouper(2, my_list))
[('a', 'b'), ('c', 'd'), ('e', 'f'), ('g', None)]

10

创建一个列表嵌套列表的另一种方法可以简化如下所示:

>>>MyList = ['a','b','c','d','e','f']
# Calculate desired row/col
>>>row = 3
>>>col = 2
>>>NewList = [MyList[col*i : col*(i+1)] for i in range(row)]
>>>NewList
[['a', 'b', 'c'], ['d', 'e', 'f']]

这种方法可以扩展以产生任何行和列的大小。如果您选择行和列的值,使得 row*col >len(MyList) ,则包含 MyList 最后一个值的子列表 (row) 将在那里结束,并且 NewList 将仅用适当数量的空列表填充以满足行/列规格。

>>>MyList = ['a','b','c','d','e','f','g','h']
>>>row = 3
>>>col = 3
>>>NewList = [MyList[col*i : col*(i+1)] for i in range(row)]
>>>NewList
[['a', 'b', 'c'], ['d', 'e', 'f'], ['g','h']]

>>>row = 4
>>>col = 4
>>>NewList = [MyList[col*i : col*(i+1)] for i in range(row)]
[['a', 'b', 'c', 'd'], ['e', 'f', 'g','h'], [], []]

3
我想那应该是被接受的答案,因为问题要求列出一系列列表而不是元组列表。或者是上面评论中Robert提供的答案。 - Costas B.

0
如果一个人更喜欢从一个平面列表中返回一个列表的列表,而不是元组的列表,则可以这样做:
    a = range(20) # sample starting list 
    b = [] # new list
    c = [] # alternate new list
    # ny is length of new list. nx length of each list within it
    nx = 5; ny = 4 
    bb = 0; ee = bb + nx # option one: sliding indeces for slices.
    for ii in range(ny-1):
        bb += nx
        ee += nx
        b.append(a[bb:ee])
        c.append(a[slice(ii*nx,nx*(ii+1))]) # option two, use slice()

我曾经尝试使用列表推导式将整个for循环缩减为一行,但并没有成功。在我使用的方式中,slice()函数几乎可以帮助你实现这一点。

这些方法相对于其他提到的方法可能有一个优点,即如果您的原始平面列表不是新的所需列表的维数的整数倍,则不会丢失任何数据。但需要注意的是,最后一个列表将比其他所有列表都要短,因为它将包含“剩余部分”。尽管如此,这两种方法都不太符合Pythonic的风格。


我不明白为什么你在第二种方法中使用 slice(),而不是切片语法。进行计算并不意味着你不能使用 a[ii*nx:nx*(ii+1)] 或其他方式。 - Blckknght

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