Python 基于另一个生成器的生成器

Python 基于另一个生成器的生成器,python,python-3.x,yield-keyword,Python,Python 3.x,Yield Keyword,我的任务其实很简单,但我不知道如何实现它。我打算在我的ML算法中使用它,但是让我们简化这个例子。假设有一个如下所示的生成器: nums = ((i+1) for i in range(4)) 上述操作将产生1、2、3和4 假设上述生成器返回单个“样本”。我想写一个生成器方法,将它们进行批处理。假设批量大小为2。因此,如果调用此新方法: def batch_generator(batch_size): do something on nums yield batches of s

我的任务其实很简单,但我不知道如何实现它。我打算在我的ML算法中使用它,但是让我们简化这个例子。假设有一个如下所示的生成器:

nums = ((i+1) for i in range(4))
上述操作将产生
1
2
3
4

假设上述生成器返回单个“样本”。我想写一个生成器方法,将它们进行批处理。假设批量大小为
2
。因此,如果调用此新方法:

def batch_generator(batch_size):
    do something on nums
    yield batches of size batch_size
然后这个批处理生成器的输出将是:
1
2
,然后是
3
4
。元组/列表并不重要。重要的是如何退回这些批次。我从Python3.3中引入的关键字中发现了这个
,但在我的例子中它似乎没有用处


显然,如果我们有
5
nums而不是
4
,并且
batch\u size
2
,我们将忽略第一个生成器中最后生成的值。

我自己的解决方案是

nums = (i+1 for i in range(4))

def giveBatch(gen, numOfItems):
    try:
        return [next(gen) for i in range(numOfItems)]
    except StopIteration:
        pass

giveBatch(nums, 2)
# [1, 2]
giveBatch(nums, 2)
# [3, 4]

另一个解决方案是使用@Bharel提到的
grouper
。我比较了运行这两种解决方案所需的时间。差别不大。我想这是可以忽略的

from timeit import timeit

def wrapper(func, *args, **kwargs):
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

nums = (i+1 for i in range(1000000))

wrappedGiveBatch = wrapper(giveBatch, nums, 2)
timeit(wrappedGiveBatch, number=1000000)
# ~ 0.998439

wrappedGrouper = wrapper(grouper, nums, 2)
timeit(wrappedGrouper, number=1000000)
# ~ 0.734342

我自己的解决办法可能是

nums = (i+1 for i in range(4))

def giveBatch(gen, numOfItems):
    try:
        return [next(gen) for i in range(numOfItems)]
    except StopIteration:
        pass

giveBatch(nums, 2)
# [1, 2]
giveBatch(nums, 2)
# [3, 4]

另一个解决方案是使用@Bharel提到的
grouper
。我比较了运行这两种解决方案所需的时间。差别不大。我想这是可以忽略的

from timeit import timeit

def wrapper(func, *args, **kwargs):
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

nums = (i+1 for i in range(1000000))

wrappedGiveBatch = wrapper(giveBatch, nums, 2)
timeit(wrappedGiveBatch, number=1000000)
# ~ 0.998439

wrappedGrouper = wrapper(grouper, nums, 2)
timeit(wrappedGrouper, number=1000000)
# ~ 0.734342
在下面有一个代码段,它只执行以下操作:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)
与每次调用一个方法不同,您有一个迭代器,它返回批处理,效率更高、速度更快,并且可以处理一些棘手的情况,例如数据太快用完而不会丢失数据。

下面有一个代码段,它可以:

from itertools import zip_longest

def grouper(iterable, n, fillvalue=None):
    "Collect data into fixed-length chunks or blocks"
    # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx"
    args = [iter(iterable)] * n
    return zip_longest(*args, fillvalue=fillvalue)

与其每次都调用一个方法,不如使用一个迭代器来返回批处理,效率更高、速度更快,并且可以处理一些特殊情况,例如数据太快用完而不会丢失数据。

这正是我所需要的:

def giveBatch(numOfItems):
    nums = (i+1 for i in range(7))

    while True:
        yield [next(nums) for i in range(numOfItems)]

这正是我所需要的:

def giveBatch(numOfItems):
    nums = (i+1 for i in range(7))

    while True:
        yield [next(nums) for i in range(numOfItems)]

请参阅
itertools
文档中的
grouper
配方。请参阅
itertools
文档中的
grouper
配方。热爱它。如果批处理没有足够的样本,则会抛出StopIteration。非常感谢@isquared KeepitReal是的,我只是补充说,欢迎:)或者不抛出:)哈哈,迭代速度提高了25%;-)我喜欢。如果批处理没有足够的样本,则会抛出StopIteration。非常感谢@isquared KeepitReal是的,我只是补充说,欢迎:)或者不抛出:)哈哈,迭代速度提高了25%;-)很高兴您找到了解决方案,但是与其定义
nums
static,不如将其传递给函数,并以更通用、更灵活的方式完成工作。通过这种方式,您可以将
giveBatch
用于任何生成器。没错,我将重构:)很高兴您找到了解决方案,但与其定义
nums
static,不如将其传递给函数,并以更通用、更灵活的方式完成工作。这样,您就可以对任何生成器使用
giveBatch