【发布时间】:2018-05-19 14:50:03
【问题描述】:
有没有简洁优雅的方式通过分隔元素将 Python 中的列表拆分为子列表列表,例如 ['a', 'delim', 'b'] -> [['a'], ['b']]?
示例如下:
ldat = ['a','b','c','a','b','c','a','b']
dlim = 'c'
lspl = [] # an elegant python one-liner wanted on this line!
print(lspl) # want: [['a', 'b'], ['a', 'b'], ['a', 'b']]
看似过于复杂的工作示例
我已经调查了有关 stackoverflow 的文档和相关问题 - 下面引用了许多 - 没有回答我的问题,我在下面总结了我的研究:确实生成所需输出但很冗长的几种方法并且错综复杂,并且正在发生的事情(拆分列表)并不是很明显 - 你真的必须眯着眼睛。
有没有更好的方法?我主要对初学者的可读性(例如教学)、规范/“Pythonic”方法感兴趣,其次对最有效的方法(例如 timeit 速度)感兴趣。理想情况下,答案将同时针对 Python 2.7 和 3.x。
有条件的.append()
遍历列表并追加到最后一个输出列表或添加新的输出列表。基于 includes the delimiter 的示例,但已更改为排除它。我不知道如何使它成为一个单线,或者这是否是可取的。
lspl = [[]]
for i in ldat:
if i==dlim:
lspl.append([])
else:
lspl[-1].append(i)
print(lspl) # prints: [['a', 'b'], ['a', 'b'], ['a', 'b']]
使用 itertools.groupby
将 itertools.groupby 与列表理解相结合。很多答案include delimeters,这是基于exclude delimeters的答案。
import itertools
lspl = [list(y) for x, y in itertools.groupby(ldat, lambda z: z == dlim) if not x]
print(lspl) # prints: [['a', 'b'], ['a', 'b'], ['a', 'b']]
对索引进行切片
一些相关问题已经讨论了在使用 .index() 之后如何使用切片——但是答案通常集中在查找 first index only 上。可以先通过finding a list of indices 扩展此方法,然后循环遍历自压缩列表以分割范围。
indices = [i for i, x in enumerate(ldat) if x == dlim]
lspl = [ldat[s+1:e] for s, e in zip([-1] + indices, indices + [len(ldat)])]
print(lspl) # prints: [['a', 'b'], ['a', 'b'], ['a', 'b']]
但是,就像我发现的所有方法一样,这似乎是一种非常复杂的方式来执行简单的分隔符拆分操作。
与字符串拆分的比较
相比之下,仅作为模型,这是一种有效、简洁且优雅的拆分方式 通过分隔符将字符串转换为子字符串列表。
sdat = 'abcabcab'
dlim = 'c'
sspl = sdat.split(dlim)
print(sspl) # prints: ['ab', 'ab', 'ab']
注意:我知道 Python 中的列表上没有 split 方法,我不是在询问拆分字符串的问题。我也不是在询问将元素字符串拆分为新元素。
【问题讨论】:
-
我想到了对索引进行切片的方法,尽管那是两行。将它包装在一个函数中:) 然后它是一行
-
“我不知道如何使它成为一个单线,或者如果这甚至是可取的。”不,它本身不是。真的,没有比 for 循环更规范的了。事实上,我看到您编写第一个示例的最大问题是将
if和else正文放在一行 - 使用缩进。 -
分隔符在开始或结束时的预期行为?
-
真的任何行为都很好。所以
['delim', 'a', 'b', 'delim']可以变成[['a'], ['b']],或[[], ['a'], ['b'], []],甚至[['a'], ['b'], []]或[[], ['a'], ['b']]。 -