Python中类似于Ruby的each_slice(count)函数的等效实现

13
什么是Python中类似于Ruby的each_slice(count)函数的等价物?我想在每次迭代中从列表中取出2个元素。比如对于[1,2,3,4,5,6],第一次迭代中我想处理1,2,然后是3,4,最后是5,6。当然,可以使用索引值来绕过这个问题。但是是否有直接的函数或方法可以直接做到这一点?

1
Mark的答案完全满足您在问题中提供的规格。然而,需要注意的是,他指定的行为与Ruby的each_slice有所偏差:如果最后一个切片比其余部分短,则会用fillvalue填充,而在Ruby的each_slice中,它仅仅是一个缩短的数组。如果您想要这种缩短的列表/可迭代行为,则Mark的答案将不起作用。 - bwv549
6个回答

10

itertools文档中有一个名为grouper的recipe可供使用:

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

使用方法如下:

>>> l = [1,2,3,4,5,6]
>>> for a,b in grouper(2, l):
>>>     print a, b

1 2
3 4
5 6

请使用 zip_longest 替代 izip_longest,用于 Python 3。 - bwv549

5
我知道有多位专家对该语言进行了回答,但我使用了一种不同的方法,利用生成器函数更易于阅读、理解和根据您的需求进行修改:
def each_slice(list: List[str], size: int):
    batch = 0
    while batch * size < len(list):
        yield list[batch * size:(batch + 1) * size]
        batch += 1   

slices = each_slice(["a", "b", "c", "d", "e", "f", "g"], 2)
print([s for s in slices])

$ [['a', 'b'], ['c', 'd'], ['e', 'f'], ['g']]

如果您需要每个切片的批量大小相同,可以使用None填充或者使用一些默认字符,只需将填充代码添加到yield中即可。如果您想要each_cons而不是批处理,则可以通过修改代码从批处理改为逐个移动来实现。


我注意到了@bwv549的回答,但我的方法使用现有的数组切片语法而不是创建一个数组并向其附加元素。 - nurettin

4

为了处理较小的剩余部分,重复 Ruby 的 each_slice 行为:

def each_slice(size, iterable):
    """ Chunks the iterable into size elements at a time, each yielded as a list.

    Example:
      for chunk in each_slice(2, [1,2,3,4,5]):
          print(chunk)

      # output:
      [1, 2]
      [3, 4]
      [5]
    """
    current_slice = []
    for item in iterable:
        current_slice.append(item)
        if len(current_slice) >= size:
            yield current_slice
            current_slice = []
    if current_slice:
        yield current_slice

以上的答案将填充最后一个列表(即 [5, None]),在某些情况下可能不是预期结果。


2

和Mark的代码相同,但改名为'each_slice',适用于Python2和3:

try:
    from itertools import izip_longest  # python 2
except ImportError:
    from itertools import zip_longest as izip_longest  # python 3

def each_slice(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)

0
对前两个的改进:如果被切片的可迭代对象不能被 n 整除,最后一个将用 None 填充到长度为 n。如果这导致类型错误,您可以进行小的更改:
def each_slice(iterable, n, fillvalue=None):
    args = [iter(iterable)] * n
    raw = izip_longest(fillvalue=fillvalue, *args)
    return [filter(None, x) for x in raw]

请记住,这将从范围中删除所有的None值,因此只应在可能导致错误的情况下使用。

0
s_size = 4
l = list(range(100))

while len(l) > 0:
    slice = [l.pop() for _e,i in enumerate(l) if i <= s_size ]
    print(slice)

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