Как выполнить цикл for в пакетном режиме?

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

Я хочу выполнить код ls.append(adapter.insert_post(коллекция, x)) с размером пакета 500, где x должен содержать 500 диктов данных. Я мог бы создать список из 500 диктов данных, используя двойной цикл for и список, а затем вставить его. Я мог бы сделать это следующим образом, есть ли лучший способ сделать это? :

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))

person Mudits    schedule 29.05.2014    source источник


Ответы (4)


Общая структура, которую я использую, выглядит так:

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, чтобы значительно упростить пакетную обработку.

person nneonneo    schedule 29.05.2014
comment
@Mudits - нарезка прощает все встроенные последовательности Python. - person mgilson; 29.05.2014
comment
в python3 используйте диапазон вместо xrange, чтобы это работало - person alex9311; 23.05.2019
comment
Я удивлен, что my_list[0:5] дает [1, 2, 3] на основе my_list = [1,2,3] (и НЕ вылетает) - person Boern; 11.10.2019

Если вы работаете с последовательностями, решение @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)

Однако будьте осторожны, эта последняя функция только "работает", если вы полностью исчерпали каждую полученную итерацию, прежде чем перейти к следующей. В крайнем случае, когда вы не исчерпаете какие-либо итерации, например. list(grouper3(..., n)) вы получите "m" итераций, которые дают только 1 элемент, а не n (где "m" - это "длина" входной итерации). Иногда такое поведение может быть полезным, но не всегда. Мы также можем исправить это, если воспользуемся рецептом itertools «consume» (который также требует импорта 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)) вернет пустые итерации — любое значение, не извлеченное из «группы» до следующего вызова next (например, когда цикл for возвращается к началу), никогда не будет возвращено.

person mgilson    schedule 29.05.2014
comment
Вы можете сократить код группировщиков 3, 4 на одну строку, проверив, существует ли элемент непосредственно в исходной итерации, а не в группе. Для grouper3 внутри цикла может быть item = next(iterable); yield chain((item,), islice(iterable, n-1)) для того же эффекта. Ваш код интуитивно понятен и разборчив, поэтому я не предлагаю вам его менять, а просто предлагаю альтернативную формулировку. - person Mad Physicist; 30.06.2017

Может быть, что-то вроде этого?

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 = []
person desired login    schedule 29.05.2014

Я думаю, что один конкретный сценарий здесь не рассматривается. Допустим, размер пакета равен 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

Вышеупомянутый список может быть отправлен на каждый вызов метода, чтобы завершить выполнение для всех элементов.

person Rajesh Somasundaram    schedule 04.06.2020