Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/284.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 将生成器拆分为多个块,而无需预先遍历它_Python_Generator - Fatal编程技术网

Python 将生成器拆分为多个块,而无需预先遍历它

Python 将生成器拆分为多个块,而无需预先遍历它,python,generator,Python,Generator,(这个问题与and有关,但这些都是在发电机前行走,这正是我想要避免的) 我想把发电机分成几块。这些要求是: 不要填充区块:如果剩余元素的数量小于区块大小,则最后一个区块必须更小 不要预先遍历生成器:计算元素是昂贵的,必须由消费函数完成,而不是由chunker完成 当然,这意味着:不要在内存中累积(没有列表) 我尝试了以下代码: def head(iterable, max=10): for cnt, el in enumerate(iterable): yield e

(这个问题与and有关,但这些都是在发电机前行走,这正是我想要避免的)

我想把发电机分成几块。这些要求是:

  • 不要填充区块:如果剩余元素的数量小于区块大小,则最后一个区块必须更小
  • 不要预先遍历生成器:计算元素是昂贵的,必须由消费函数完成,而不是由chunker完成
  • 当然,这意味着:不要在内存中累积(没有列表)
我尝试了以下代码:

def head(iterable, max=10):
    for cnt, el in enumerate(iterable):
        yield el
        if cnt >= max:
            break

def chunks(iterable, size=10):
    i = iter(iterable)
    while True:
        yield head(i, size)

# Sample generator: the real data is much more complex, and expensive to compute
els = xrange(7)

for n, chunk in enumerate(chunks(els, 3)):
    for el in chunk:
        print 'Chunk %3d, value %d' % (n, el)
这一点在某种程度上是有效的:

Chunk   0, value 0
Chunk   0, value 1
Chunk   0, value 2
Chunk   1, value 3
Chunk   1, value 4
Chunk   1, value 5
Chunk   2, value 6
^CTraceback (most recent call last):
  File "xxxx.py", line 15, in <module>
    for el in chunk:
  File "xxxx.py", line 2, in head
    for cnt, el in enumerate(iterable):
KeyboardInterrupt
但是,异常只在消费者的上下文中出现,这不是我想要的(我想保持消费者代码的干净)

Chunk 0,值0
块0,值1
块0,值2
块1,值3
块1,值4
块1,值5
块2,值6
回溯(最近一次呼叫最后一次):
文件“xxxx.py”,第22行,在
对于区块中的el:
文件“xxxx.py”,第9行,头部
如果cnt==0:提高NoMoreData
__main_uu.NoMoreData()

如何检测生成器在
chunks
函数中是否已耗尽,而无需遍历它?

一种方法是查看第一个元素(如果有),然后创建并返回实际的生成器

def head(iterable, max=10):
    first = next(iterable)      # raise exception when depleted
    def head_inner():
        yield first             # yield the extracted first element
        for cnt, el in enumerate(iterable):
            yield el
            if cnt + 1 >= max:  # cnt + 1 to include first
                break
    return head_inner()
只需在
生成器中使用它,并像处理自定义异常一样捕获
停止迭代
异常


更新:这里是另一个版本,用于替换大部分
头部
功能和
for
循环。事实上,这个简单的
for
循环与笨拙的
在尝试下一步时做的事情完全相同,只是在原始代码中使用了break
构造,因此结果更具可读性

def chunks(iterable, size=10):
    iterator = iter(iterable)
    for first in iterator:    # stops when iterator is depleted
        def chunk():          # construct generator for next chunk
            yield first       # yield element from for loop
            for more in islice(iterator, size - 1):
                yield more    # yield more elements from the iterator
        yield chunk()         # in outer generator, yield next chunk
我们甚至可以用更短的时间来替换内部发电机:

def chunks(iterable, size=10):
    iterator = iter(iterable)
    for first in iterator:
        yield chain([first], islice(iterator, size - 1))
如何使用:

其中:

[0, 1]
[2, 3]
[4, 5]
[6]

您说过不希望在内存中存储内容,那么这是否意味着您无法为当前块构建中间列表

为什么不遍历生成器并在块之间插入sentinel值?消费者(或合适的包装商)可以忽略哨兵:

class Sentinel(object):
    pass

def chunk(els, size):
    for i, el in enumerate(els):
        yield el
        if i > 0 and i % size == 0:
            yield Sentinel

使用生成器生成器编辑其他解决方案

您不应该在迭代器中执行
while True
,而只需在每次迭代时对其进行迭代并更新区块编号:

def chunk(it, maxv):
    n = 0
    for i in it:
        yield n // mavx, i
        n += 1
如果您想要一台发电机,您可以:

def chunk(a, maxv):
    def inner(it, maxv, l):
        l[0] = False
        for i in range(maxv):
            yield next(it)
        l[0] = True
        raise StopIteration
    it = iter(a)
    l = [True]
    while l[0] == True:
        yield inner(it, maxv, l)
    raise StopIteration
带着一个可爱的小女孩

测试:在python 2.7和3.4上:

for i in chunk(range(7), 3):
    print 'CHUNK'
    for a in i:
        print a
给出:

CHUNK
0
1
2
CHUNK
3
4
5
CHUNK
6
第2.7节:

for i in chunk(xrange(7), 3):
    print 'CHUNK'
    for a in i:
        print a
给出了相同的结果


但是注意
列表(chunk(range(7))
2.7和3.4上的块

由于(在CPython中)使用纯C级内置,我可以想出最快的解决方案。通过这样做,不需要Python字节码来生成每个块(除非底层生成器是用Python实现的)这对性能有巨大的好处。它在返回块之前会遍历每个块,但不会在即将返回的块之外进行任何预遍历:

# Py2 only to get generator based map
from future_builtins import map

from itertools import islice, repeat, starmap, takewhile
# operator.truth is *significantly* faster than bool for the case of
# exactly one positional argument
from operator import truth

def chunker(n, iterable):  # n is size of each chunk; last chunk may be smaller
    return takewhile(truth, map(tuple, starmap(islice, repeat((iter(iterable), n)))))
因为这有点密集,所以用于说明的展开版本:

def chunker(n, iterable):
    iterable = iter(iterable)
    while True:
        x = tuple(islice(iterable, n))
        if not x:
            return
        yield x

enumerate
中包装对
chunker
的调用可以让您根据需要对块进行编号。

我也遇到了同样的问题,但找到了比这里提到的更简单的解决方案:

def chunker(iterable, chunk_size):
    els = iter(iterable)
    while True:
        next_el = next(els)
        yield chain([next_el], islice(els, chunk_size - 1))

for i, chunk in enumerate(chunker(range(11), 2)):
    for el in chunk:
        print(i, el)

# Prints the following:
0 0
0 1
1 2
1 3
2 4
2 5
3 6
3 7
4 8
4 9
5 10
从itertools导入islice
def块(it,n):
'''
#返回每个元素的n个块
>>>列表(块(范围(10),3))
[
[0, 1, 2, ],
[3, 4, 5, ],
[6, 7, 8, ],
[9, ]
]
>>>列表(块(列表(范围(10)),3))
[
[0, 1, 2, ],
[3, 4, 5, ],
[6, 7, 8, ],
[9, ]
]
'''
定义w(g):
返回lambda:tuple(islice(g,n))
返回iter(_w(iter(it)),())

生成器在使用对象的键函数上使用的另一种创建组/块而不是预走的方法。由于
计数
对象独立于iterable,因此可以在不知道iterable包含什么的情况下轻松生成块

groupby
的每次迭代都会调用
count
对象的
next
方法,并通过将当前计数值除以区块大小生成一个组/区块键(后跟区块中的项目)

from itertools import groupby, count

def chunks(iterable, size=10):
    c = count()
    for _, g in groupby(iterable, lambda _: next(c)//size):
        yield g

生成器函数生成的每个组/块
g
都是一个迭代器。但是,由于
groupby
对所有组使用一个共享迭代器,因此组迭代器不能存储在列表或任何容器中,每个组迭代器都应该在下一个之前使用。

在crafting以更高速度插入500k+行的DB解决方案

一个生成器处理来自源的数据,并逐行“生成”它;然后另一个生成器将输出分组,并逐块“生成”它。第二个生成器只知道块大小,仅此而已

以下是强调该概念的示例:

#!/usr/bin/python

def firstn_gen(n):
    num = 0
    while num < n:
        yield num
        num += 1

def chunk_gen(some_gen, chunk_size=7):
    res_chunk = []
    for count, item in enumerate(some_gen, 1):
        res_chunk.append(item)
        if count % chunk_size == 0:
            yield res_chunk
            res_chunk[:] = []
    else:
        yield res_chunk


if __name__ == '__main__':
    for a_chunk in chunk_gen(firstn_gen(33)):
        print(a_chunk)
受此启发,我试图制定一个解决方案,在每一步都使用但不需要划分

下面的函数可以用作groupby的键,它只返回一个布尔值,在预定义的调用次数后会翻转

def chunks(chunksize=3):

    def flag_gen():
        flag = False
        while True:
            for num in range(chunksize):
                yield flag
            flag = not flag

    flag_iter = flag_gen()

    def flag_func(*args, **kwargs):
        return next(flag_iter)

    return flag_func
可以这样使用:

from itertools import groupby

my_long_generator = iter("abcdefghijklmnopqrstuvwxyz")

chunked_generator = groupby(my_long_generator, key=chunks(chunksize=5))

for flag, chunk in chunked_generator:
    print("Flag is {f}".format(f=flag), list(chunk))
输出:

Flag is False ['a', 'b', 'c', 'd', 'e']
Flag is True ['f', 'g', 'h', 'i', 'j']
Flag is False ['k', 'l', 'm', 'n', 'o']
Flag is True ['p', 'q', 'r', 's', 't']
Flag is False ['u', 'v', 'w', 'x', 'y']
Flag is True ['z']
我已经做了一个。

提供了一个可以实现目标的方法,上面提到了这一点


我不知道如何修复它,但是除非在创建
头时引发异常,而不是在迭代时引发异常,
之外的
才会捕获异常。@tobias_k:当然,我理解。我正在寻找修复方法……查看第一个元素可以吗?您可以尝试
下一个
第一个元素,然后引发一个异常异常或返回实际的块迭代器。@tobias_k:这是一个很好的折衷方案,但不是
[0, 1, 2, 3, 4, 5, 6]
[7, 8, 9, 10, 11, 12, 13]
[14, 15, 16, 17, 18, 19, 20]
[21, 22, 23, 24, 25, 26, 27]
[28, 29, 30, 31, 32]
def chunks(chunksize=3):

    def flag_gen():
        flag = False
        while True:
            for num in range(chunksize):
                yield flag
            flag = not flag

    flag_iter = flag_gen()

    def flag_func(*args, **kwargs):
        return next(flag_iter)

    return flag_func
from itertools import groupby

my_long_generator = iter("abcdefghijklmnopqrstuvwxyz")

chunked_generator = groupby(my_long_generator, key=chunks(chunksize=5))

for flag, chunk in chunked_generator:
    print("Flag is {f}".format(f=flag), list(chunk))
Flag is False ['a', 'b', 'c', 'd', 'e']
Flag is True ['f', 'g', 'h', 'i', 'j']
Flag is False ['k', 'l', 'm', 'n', 'o']
Flag is True ['p', 'q', 'r', 's', 't']
Flag is False ['u', 'v', 'w', 'x', 'y']
Flag is True ['z']