如何分批执行for循环?

问题描述 投票:13回答:4
for x in records:
   data = {}
   for y in sObjectName.describe()['fields']
         data[y['name']] = x[y['name']]
   ls.append(adapter.insert_posts(collection, data))

我想在500个批次中执行代码ls.append(adapter.insert_post(collection, x)),其中x应该包含500个data dicts。我可以使用一个双倍for循环和一个list创建一个包含500个dicts的list a,然后插入它。我可以用下面的方法来做, ,有没有更好的方法?

for x in records:
    for i in xrange(0,len(records)/500):
        for j in xrange(0,500):
            l=[]
            data = {}
            for y in sObjectName.describe()['fields']:
                data[y['name']] = x[y['name']]
                #print data
            #print data
            l.append(data)
        ls.append(adapter.insert_posts(collection, data))

    for i in xrange(0,len(records)%500):
        l=[]
        data = {}
        for y in sObjectName.describe()['fields']:
            data[y['name']] = x[y['name']]
            #print data
        #print data
        l.append(data)
    ls.append(adapter.insert_posts(collection, data))
python python-2.7 batch-processing
4个回答
33
投票

我使用的一般结构是这样的。

worklist = [...]
batchsize = 500

for i in xrange(0, len(worklist), batchsize):
    batch = worklist[i:i+batchsize] # the result might be shorter than batchsize at the end
    # do stuff with batch

注意,我们使用的是 step 的论点 xrange 来大大简化批处理。


7
投票

如果你正在处理序列,@nneonneo 的解决方案是你能得到的最好的性能。 如果你想要一个可以处理任意迭代的解决方案,你可以看看一些由 @nneonneo 提出的 itertools 食谱.如石斑鱼。

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 itertools.izip_longest(fillvalue=fillvalue, *args)

我倾向于不用这个,因为它把最后一个组 "填满 "了。None 以使其与其他变量的长度相同。 我通常定义我自己的变体,它没有这种行为。

def grouper2(iterable, n):
    iterable = iter(iterable)
    while True:
        tup = tuple(itertools.islice(iterable, 0, n))
        if tup:
            yield tup
        else:
            break

这将产生符合要求大小的元组。 这通常已经足够好了,但是,为了好玩,我们可以写一个生成器,如果我们真的想的话,它可以返回正确大小的懒惰迭代。

我认为这里的 "最佳 "解决方案取决于手头的问题--尤其是原始迭代中的组和对象的大小以及原始迭代的类型。 一般来说,最后这2个配方会发现用处不大,因为它们比较复杂,很少需要。 然而,如果你觉得很冒险,想找点乐子,请继续阅读


为了得到一个懒惰的可迭代而不是元组,我们需要做的唯一真正的修改就是能够 "偷看 "下一个值。islice 看看是否有什么东西在那里。 在这里,我只是偷看的值 - 如果它的丢失。StopIteration 将会升高,这将使发电机停止,就像它正常结束一样。 如果它在那里,我就把它放回去,用 itertools.chain:

def grouper3(iterable, n):
    iterable = iter(iterable)
    while True:
        group = itertools.islice(iterable, n)
        item = next(group)  # raises StopIteration if the group doesn't yield anything
        yield itertools.chain((item,), group)

但要小心,最后一个功能 只是 "工作",如果你完全穷尽每一个可迭代的产物。之前 继续下一个。 在极端的情况下,如果你没有用尽任何一个迭代项,例如,你会得到 "m "个迭代项,而这些迭代项只能产生一个项目,而不是一个项目。list(grouper3(..., n)),你会得到 "m "个迭代项,这些迭代项只产生1个项目,而不是 n (其中 "m "是输入迭代的 "长度")。 这种行为实际上有时会很有用,但通常不会。 如果我们使用itertools的 "消耗 "配方,我们也可以解决这个问题(这也需要引入 collections 除了 itertools):

def grouper4(iterable, n):
    iterable = iter(iterable)
    group = []
    while True:
        collections.deque(group, maxlen=0)  # consume all of the last group
        group = itertools.islice(iterable, n)
        item = next(group)  # raises StopIteration if the group doesn't yield anything
        group = itertools.chain((item,), group)
        yield group

当然可以 list(grouper4(..., n)) 将返回空的iterables -- 在下一次调用 "组 "之前没有从 "组 "中提取的任何值。next (例如,当 for 循环循环回到起点)将永远不会得到收益。


2
投票

也许是这样的?

l = []
for ii, x in enumerate(records):
    data = {}
    for y in sObjectName.describe()['fields']
        data[y['name']] = x[y['name']]
    l.append(data)
    if not ii % 500:
        ls.append(adapter.insert_posts(collection, l))
        l = []

1
投票

我认为这里没有涉及到一个特殊的情况。假设批处理量是100,而你的列表大小是103,上面的答案可能会漏掉最后3个元素。

list = [.....] 103 elements
total_size = len(list)
batch_size_count=100

for start_index in range(0, total_size, batch_size_count):
    end_index = total_size if start_index + batch_size_count > total_size else start_index + batch_size_count
    list[start_index:end_index] #Slicing operation

上面的分片列表可以发送给每个方法调用,完成所有元素的执行。

© www.soinside.com 2019 - 2024. All rights reserved.