Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/372.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中按块(共n个)迭代迭代器?_Python_Iterator - Fatal编程技术网

在Python中按块(共n个)迭代迭代器?

在Python中按块(共n个)迭代迭代器?,python,iterator,Python,Iterator,你能想出一个很好的方法(可能是使用itertools)将迭代器分割成给定大小的块吗 因此l=[1,2,3,4,5,6,7]与块(l,3)一起成为迭代器[1,2,3],[4,5,6],[7] 我可以想出一个小程序来实现这一点,但这不是一个很好的使用itertools的方法。来自itertools文档的grouper()配方接近您想要的: def grouper(n, iterable, fillvalue=None): "grouper(3, 'ABCDEFG', 'x') --

你能想出一个很好的方法(可能是使用itertools)将迭代器分割成给定大小的块吗

因此
l=[1,2,3,4,5,6,7]
块(l,3)
一起成为迭代器
[1,2,3],[4,5,6],[7]

我可以想出一个小程序来实现这一点,但这不是一个很好的使用itertools的方法。

来自
itertools
文档的
grouper()
配方接近您想要的:

def grouper(n, iterable, fillvalue=None):
    "grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return izip_longest(fillvalue=fillvalue, *args)
不过,它会用一个填充值填充最后一个块

一个不太通用的解决方案是,它只对序列有效,但能根据需要处理最后一个块

[my_list[i:i + chunk_size] for i in range(0, len(my_list), chunk_size)]
def grouper(n, iterable):
    it = iter(iterable)
    while True:
        chunk = tuple(itertools.islice(it, n))
        if not chunk:
            return
        yield chunk
最后,在通用迭代器上工作的解决方案是

[my_list[i:i + chunk_size] for i in range(0, len(my_list), chunk_size)]
def grouper(n, iterable):
    it = iter(iterable)
    while True:
        chunk = tuple(itertools.islice(it, n))
        if not chunk:
            return
        yield chunk
“简单总比复杂好”- 一个简单的发电机几行就可以完成这项工作。只需将其放在一些实用程序模块中即可:

def grouper (iterable, n):
    iterable = iter(iterable)
    count = 0
    group = []
    while True:
        try:
            group.append(next(iterable))
            count += 1
            if count % n == 0:
                yield group
                group = []
        except StopIteration:
            yield group
            break

这里有一个函数返回惰性块;如果需要列表,请使用
映射(列表、块(…)

from itertools import islice, chain
from collections import deque

def chunks(items, n):
    items = iter(items)
    for first in items:
        chunk = chain((first,), islice(items, n-1))
        yield chunk
        deque(chunk, 0)

if __name__ == "__main__":
    for chunk in map(list, chunks(range(10), 3)):
        print chunk

    for i, chunk in enumerate(chunks(range(10), 3)):
        if i % 2 == 1:
            print "chunk #%d: %s" % (i, list(chunk))
        else:
            print "skipping #%d" % i

我忘了是从哪里找到灵感的。我对其进行了一些修改,以便在Windows注册表中使用MSI GUID:

def nslice(s, n, truncate=False, reverse=False):
    """Splits s into n-sized chunks, optionally reversing the chunks."""
    assert n > 0
    while len(s) >= n:
        if reverse: yield s[:n][::-1]
        else: yield s[:n]
        s = s[n:]
    if len(s) and not truncate:
        yield s
reverse
不适用于您的问题,但我在这个函数中广泛使用它

>>> [i for i in nslice([1,2,3,4,5,6,7], 3)]
[[1, 2, 3], [4, 5, 6], [7]]
>>> [i for i in nslice([1,2,3,4,5,6,7], 3, truncate=True)]
[[1, 2, 3], [4, 5, 6]]
>>> [i for i in nslice([1,2,3,4,5,6,7], 3, truncate=True, reverse=True)]
[[3, 2, 1], [6, 5, 4]]
给你

def chunksiter(l, chunks):
    i,j,n = 0,0,0
    rl = []
    while n < len(l)/chunks:        
        rl.append(l[i:j+chunks])        
        i+=chunks
        j+=j+chunks        
        n+=1
    return iter(rl)


def chunksiter2(l, chunks):
    i,j,n = 0,0,0
    while n < len(l)/chunks:        
        yield l[i:j+chunks]
        i+=chunks
        j+=j+chunks        
        n+=1

一个简洁的实现是:

chunker = lambda iterable, n: (ifilterfalse(lambda x: x == (), chunk) for chunk in (izip_longest(*[iter(iterable)]*n, fillvalue=())))
这是因为
[iter(iterable)]*n
是一个包含相同迭代器n次的列表;压缩从列表中的每个迭代器(即相同的迭代器)中获取一个项,结果是每个zip元素包含一组
n

需要使用
izip_longest
来完全使用底层iterable,而不是在到达第一个耗尽的迭代器时停止迭代,这将从
iterable
中删除任何剩余部分。这导致需要过滤掉填充值。因此,更稳健的实施方案是:

def chunker(iterable, n):
    class Filler(object): pass
    return (ifilterfalse(lambda x: x is Filler, chunk) for chunk in (izip_longest(*[iter(iterable)]*n, fillvalue=Filler)))
这保证了填充值永远不会是基础iterable中的项。使用上述定义:

iterable = range(1,11)

map(tuple,chunker(iterable, 3))
[(1, 2, 3), (4, 5, 6), (7, 8, 9), (10,)]

map(tuple,chunker(iterable, 2))
[(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]

map(tuple,chunker(iterable, 4))
[(1, 2, 3, 4), (5, 6, 7, 8), (9, 10)]

此实现几乎满足了您的需求,但存在以下问题:

def chunks(it, step):
  start = 0
  while True:
    end = start+step
    yield islice(it, start, end)
    start = end
(区别在于,
islice
不会对超出
it
结尾的调用引发StopIteration或任何其他调用,这将永远产生;还有一个稍微棘手的问题,即在迭代此生成器之前必须使用
islice
结果)

要按功能生成移动窗口,请执行以下操作:

izip(count(0, step), count(step, step))
这就变成了:

(it[start:end] for (start,end) in izip(count(0, step), count(step, step)))
但是,这仍然会创建一个无限迭代器。因此,您需要花些时间(或者其他更好的方法)来限制它:

chunk = lambda it, step: takewhile((lambda x: len(x) > 0), (it[start:end] for (start,end) in izip(count(0, step), count(step, step))))

g = chunk(range(1,11), 3)

tuple(g)
([1, 2, 3], [4, 5, 6], [7, 8, 9], [10])


虽然OP要求函数以列表或元组的形式返回块,但若需要返回迭代器,则可以修改解决方案:

def grouper_it(n, iterable):
    it = iter(iterable)
    while True:
        chunk_it = itertools.islice(it, n)
        try:
            first_el = next(chunk_it)
        except StopIteration:
            return
        yield itertools.chain((first_el,), chunk_it)
一些基准:


只有当您的函数遍历每个块中的元素时,它的效率才会稍微提高。

我今天正在做一些工作,并提出了一个简单的解决方案。这与答案类似,但我相信当
iterable
的长度可被
n
整除时,his将产生空的
group
s。当
iterable
用完时,我的答案会进行简单的检查

def chunk(iterable, chunk_size):
    """Generate sequences of `chunk_size` elements from `iterable`."""
    iterable = iter(iterable)
    while True:
        chunk = []
        try:
            for _ in range(chunk_size):
                chunk.append(iterable.next())
            yield chunk
        except StopIteration:
            if chunk:
                yield chunk
            break

这将适用于任何iterable。它返回生成器中的生成器(以获得充分的灵活性)。我现在意识到它基本上与@reclosedevs解决方案相同,但没有缺点。不需要
try…除非
StopIteration
向上传播,这正是我们想要的

当iterable为空时,需要调用
next(iterable)
来引发
StopIteration
,因为如果您允许的话,
islice
将继续生成空生成器

它更好,因为它只有两行,但很容易理解

def grouper(iterable, n):
    while True:
        yield itertools.chain((next(iterable),), itertools.islice(iterable, n-1))

请注意,
next(iterable)
被放入一个元组中。否则,如果
next(iterable)
本身是iterable,那么
itertools.chain
将使其变平。感谢Jeremy Brown指出了这个问题。

@kindall:这很接近,但不一样,因为处理了最后一个块。这有点不同,因为那个问题是关于列表的,而这个问题更一般,迭代器。虽然答案看起来是一样的。@递归:是的,在完全阅读链接线程后,我发现我的答案中的所有内容都已经出现在另一个线程中。这个答案与我开始的答案很接近,但不完全相同:这只适用于序列,而不适用于一般的iterables。@SvenMarnach:Hi Sven,是的,谢谢,你完全正确。我看到了OP的例子,它使用了一个列表(序列)并掩盖了问题的措辞,假设它们是指序列。不过,谢谢你指出这一点。当我看到你的评论时,我并没有立即理解其中的区别,但后来我查阅了它<代码>:)
请注意如何工作。注意:此生成器生成的iterable仅在请求下一个iterable之前保持有效。当使用例如
列表(块(范围(10),3))
时,所有的iterables都已被使用。这只适用于序列,不适用于一般的iterables。1。第一个代码段包含行
start=end
,该行似乎没有做任何事情,因为循环的下一次迭代将以
start=0
开始。此外,循环是无限的——它是
而True
没有任何
中断
。2.第二个代码段中的
len
是什么?3.所有其他实现仅适用于序列,而不适用于一般迭代器。4.检查
x是()
依赖于CPython的实现细节。作为一种优化,空元组只创建一次,以后再重用。但是,语言规范并不能保证这一点,因此您应该使用
x==()
.5。
count()
takewhile()
的组合使用
更容易实现