使用`itertools.groupby()`来获取以`A`开头的字符串运行列表?

时间:2015-10-02 21:21:11

标签: python itertools

(抽象)问题是:我有一个日志文件

A: 1
A: 2
A: 3
B: 4
B: 5
A: 6
C: 7
D: 8
A: 9
A: 10
A: 11

我希望得到一个像这样的列表列表:

[["1", "2", "3"], ["6"], ["9", "10", "11"]]

文件被分解为"运行"以A开头的字符串。我知道我可以使用itertools.groupby来解决这个问题,现在我有了这个解决方案(其中f是文件中的行列表)。

starts_with_a = lambda x: x.startswith("A")
coalesced = [g for _, g in groupby(f), key=starts_with_a]
runs = [re.sub(r'A: ', '', s) for s in coalesced if starts_with_a(s)]

所以我使用groupby,但后来我必须过滤掉那些不以" A"开头的东西。这没关系,而且相当简洁,但有更优雅的方式吗?我喜欢这样一种方式:

  • 不需要两次通过
  • 更简洁(和/或)更具可读性

帮助我利用itertools

的威力

4 个答案:

答案 0 :(得分:2)

是的,过滤掉不以A开头的行,但使用groupby()为返回的每个组生成的。它是key函数的返回值,因此对于那些以True开头的行,它会A。我在这里使用str.partition()而不是正则表达式:

coalesce = (g for key, g in groupby(f, key=lambda x: x[:1] == "A") if key)
runs = [[res.partition(':')[-1].strip() for res in group] for group in coalesce]

由于你的str.startswith()参数是一个固定宽度的字符串文字,你也可以使用切片;第一个字符的x[:1]个切片,并将其与'A'进行比较,从而为您提供与x.startswith('A')相同的测试。

我使用生成器表达式对groupby()过滤进行分组;你可以只是将其内联到一个列表理解中:

runs = [[res.partition(':')[-1].strip() for res in group]
        for key, group in groupby(f, key=lambda x: x[:1] == "A") if key]

演示:

>>> from itertools import groupby
>>> f = '''\
... A: 1
... A: 2
... A: 3
... B: 4
... B: 5
... A: 6
... C: 7
... D: 8
... A: 9
... A: 10
... A: 11
... '''.splitlines(True)
>>> coalesce = (g for key, g in groupby(f, key=lambda x: x[:1] == "A") if key)
>>> [[res.partition(':')[-1].strip() for res in group] for group in coalesce]
[['1', '2', '3'], ['6'], ['9', '10', '11']]

答案 1 :(得分:2)

你想要简洁吗?好的,你明白了。

>>> lst = ['A: 1', 'A: 2', 'A: 3', 'B: 4', 'B: 5', 'A: 6', 'C: 7', 'D: 8', 'A: 9', 'A: 10', 'A: 11']
>>> [[x[1] for x in group[1]] for group in itertools.groupby((line.split(': ') for line in lst), key=lambda a:a[0]) if group[0]=='A']
[['1', '2', '3'], ['6'], ['9', '10', '11']]

从内到外打破它:

(line.split(': ') for line in lst)

这是一个生成器表达式,它将每个元素拆分为其alpha键和关联的字符串值。

for group in itertools.groupby(..., key=lambda a:a[0])

这只是按照第一步中确定的字母键对元素进行分组。

... if group[0]=='A'

这只是排除了与问题中指定的条件不匹配的任何结果。如果字符串不是单个字符,您也可以使用if not group[0].startswith('A')

[x[1] for x in group[1]] for ...]

这是一个列表理解,它根据与groupby匹配早期条件的结果构建列表。 groupby返回迭代器作为第二个返回值(group[1]),因此我们只需将迭代器转换为具有列表推导的列表。 x[0]是关键值,x[1]是跟随它的字符串。

[...]

所需的输出是一个列表,因此列表理解就是这样。整个操作只需输入一次即可完成。

答案 2 :(得分:1)

在没有itertools的情况下,在一个循环中可能不是那么pythonic方式:

lines = '''
A: 1
A: 2
A: 3
B: 4
B: 5
A: 6
C: 7
D: 8
A: 9
A: 10
A: 11
'''

res = []
cont_last = []
for line in lines.splitlines():
    if line.startswith('A: '):
        cont_last.append(line.replace('A: ', ''))
    else:
        if cont_last:
            res.append(cont_last)
        cont_last = []

if cont_last:
    res.append(cont_last)    

print(res)

答案 3 :(得分:1)

不需要itertools,只需一次迭代即可完成整个文件:

 lines = open('logfile.txt','r').readlines()

 out_list = []
 temp_list = []

 for line in lines:

     if line.split(':')[0].strip() == 'A':
          temp_list.append(line.split(':')[1].strip())

     elif temp_list:
          out_list.append(temp_list)
          temp_list = []

 if temp_list:
     out_list.append(temp_list)
     temp_list = []

 print (out_list)

我知道你要求itertools我没有把它放在手边,所以我无法调试它。希望这会有所帮助。