{a1的例子显示了如何在一个连续的序列中找到一个。引用:Find runs of consecutive numbers using groupby. The key to the
solution is differencing with a range so that consecutive numbers all
appear in same group.
出于某种奇怪的原因,这个例子在后来的文档版本中没有出现。该代码适用于数字序列,下面的代码展示了如何调整它来处理字母。在from itertools import groupby
s = 'jaghiuuabc'
def keyfunc(t):
''' Subtract the character's index in the string
from its Unicode codepoint number.
'''
i, c = t
return ord(c) - i
a = []
for k, g in groupby(enumerate(s), key=keyfunc):
# Extract the chars from the (index, char) tuples in the group
seq = [t[1] for t in g]
if len(seq) > 1:
a.append(''.join(seq))
print(a)
输出
^{pr2}$
工作原理
这个代码的核心是groupby(enumerate(s), key=keyfunc)
^{}为s中的每个字符生成包含索引号和字符的元组。例如:s = 'ABCEF'
for t in enumerate(s):
print(t)
输出(0, 'A')
(1, 'B')
(2, 'C')
(3, 'E')
(4, 'F')
^{}从序列或迭代器中获取项,并将相邻的相等项聚合为组。默认情况下,它只是比较这些项的值,看它们是否相等。但你也可以给它一个关键功能。执行此操作时,它将每个项传递给键函数,并将该键函数返回的结果用于其相等性测试。在
下面是一个简单的例子。{cd5>我们先用一个除法来定义一个整数。这基本上去掉了数字中的最后一个数字。在def div_by_10(n):
return n // 10
a = [2, 5, 10, 13, 17, 21, 22, 29, 33, 35]
b = [div_by_10(u) for u in a]
print(a)
print(b)
输出[2, 5, 10, 13, 17, 21, 22, 29, 33, 35]
[0, 0, 1, 1, 1, 2, 2, 2, 3, 3]
因此,如果我们使用div_by_10作为groupby的关键函数,它将忽略每个数字中的最后一个数字,因此,如果相邻的数字只在最后一个数字上有所不同,它就会将它们组合在一起。在from itertools import groupby
def div_by_10(n):
return n // 10
a = [2, 5, 10, 13, 17, 21, 22, 29, 33, 35]
print(a)
for key, group in groupby(a, key=div_by_10):
print(key, list(group))
输出[2, 5, 10, 13, 17, 21, 22, 29, 33, 35]
0 [2, 5]
1 [10, 13, 17]
2 [21, 22, 29]
3 [33, 35]
My keyfunc接收一个(index_number,character)元组,并从字符的代码号中减去该索引_号并返回结果。让我们看看这对我前面的'ABCEF'的示例有何作用:def keyfunc(t):
i, c = t
return ord(c) - i
for t in enumerate('ABCEF'):
print(t, keyfunc(t))
输出(0, 'A') 65
(1, 'B') 65
(2, 'C') 65
(3, 'E') 66
(4, 'F') 66
对于代码,我们用65减去代码。但我们跳过了“D”,所以当我们对“E”和“F”做减法时,我们得到了66。这就是groupby如何将“A”、“B”、“C”放在一个组中,“E”和“F”放在下一组中。在
这可能是个棘手的问题。别指望马上就能完全理解。但如果你自己做一些实验,我相信它会逐渐深入人心。;)
为了好玩,下面是该代码的不可读的多嵌套列表理解版本。;)print([z for _, g in groupby(enumerate(s),lambda t:ord(t[1])-t[0])for z in[''.join([*zip(*g)][1])]if len(z)>1])
这是另一个受Amit Tripathi's answer启发的版本。这个不使用任何导入,因为它手动进行分组。prev包含前一个字符的码位号。我们将prev初始化为-2,这样在第一次执行if i != prev + 1测试时,它保证为真,因为ord(ch)的最小可能值为零,因此将向groups添加一个新的空列表。在s = 'jaghiuuabcxyzq'
prev, groups = -2, []
for ch in s:
i = ord(ch)
if i != prev + 1:
groups.append([])
groups[-1].append(ch)
prev = i
print(groups)
a = [''.join(u) for u in groups if len(u) > 1]
print(a)
输出[['j'], ['a'], ['g', 'h', 'i'], ['u'], ['u'], ['a', 'b', 'c'], ['x', 'y', 'z'], ['q']]
['ghi', 'abc', 'xyz']