如何批量执行for循环?

Mud*_*its 12 python batch-processing python-2.7

for x in records:
   data = {}
   for y in sObjectName.describe()['fields']
         data[y['name']] = x[y['name']]
   ls.append(adapter.insert_posts(collection, data))
Run Code Online (Sandbox Code Playgroud)

我想以批量大小500执行代码ls.append(adapter.insert_post(collection,x)),其中x应包含500个数据dicts.我可以使用double for循环和列表创建500个数据dicts的列表,然后插入它.我可以通过以下方式做到这一点,有没有更好的方法呢?:

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))
Run Code Online (Sandbox Code Playgroud)

nne*_*neo 27

我使用的一般结构如下所示:

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
Run Code Online (Sandbox Code Playgroud)

请注意,我们正在使用step参数xrange来大大简化批处理.

  • 在python3中使用range而不是xrange来工作 (2认同)

mgi*_*son 7

如果你正在使用序列,那么@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)
Run Code Online (Sandbox Code Playgroud)

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

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

这会产生所请求大小的元组.这通常是足够好的,但是,为了一点乐趣,我们可以写一个生成器,如果我们真的想要...返回正确大小的懒惰迭代

我认为这里的"最佳"解决方案取决于手头的问题 - 特别是原始可迭代中的组和对象的大小以及原始可迭代的类型.一般来说,最后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)
Run Code Online (Sandbox Code Playgroud)

但是要小心,如果在完成下一个迭代之前完全耗尽每个迭代,那么最后一个函数只能 "起作用" .在没有耗尽任何迭代的极端情况下,例如,你将得到"m"个迭代,它只产生1个项目,而不是(其中"m"是输入迭代的"长度").这种行为有时可能很有用,但通常不常见.如果我们使用itertools"consume"配方(除此之外还需要导入),我们也可以解决这个问题:list(grouper3(..., n))ncollectionsitertools

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
Run Code Online (Sandbox Code Playgroud)

当然,list(grouper4(..., n))将返回空的迭代 - 在下一次调用之前未从"组"中拉出的任何值next(例如,当for循环循环回到开始时)将永远不会产生.


Tim*_*ski 5

我喜欢@nneonneo 和@mgilson 的答案,但一遍又一遍地这样做很乏味。python3 中 itertools 页面的底部提到了该库more-itertools(我知道这个问题是关于 python2 的,这是 python3 库,但有些人可能会发现这很有用)。以下似乎可以满足您的要求:

from more_itertools import chunked # Note: you might also want to look at ichuncked

for batch in chunked(records, 500):
    # Do the work--`batch` is a list of 500 records (or less for the last batch).  
Run Code Online (Sandbox Code Playgroud)