Python: эффективный способ разбить список строк на более мелкие фрагменты по объединенному размеру.

Я общаюсь с Google API через пакетные запросы через его google-api-python-client. В пакетных запросах есть ограничения:

  • Пакетный запрос не может содержать более 1000 запросов,
  • Пакетный запрос не может содержать более 1 МБ полезной нагрузки.

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

Кто-нибудь знает хороший способ эффективно создавать фрагменты этого исходного списка, которые можно отправить в API Google? Под «эффективно» я подразумеваю отсутствие повторения всех элементов из первой части (с учетом размера полезной нагрузки).

Пока это то, что я имел в виду: взять максимум 1000 штук предметов, построить запрос, посмотреть размер полезной нагрузки. Если больше 1М, берите 500, смотрите размер. Если полезная нагрузка больше, возьмите первые 250 элементов. Если полезная нагрузка меньше, берите 750 шт. И так далее, вы поняли логику. Таким образом, можно было бы получить нужное количество элементов с меньшим количеством итераций, чем создание полезной нагрузки, проверяя ее после каждого добавления.

Я действительно не хочу изобретать велосипед, поэтому, если кто-нибудь знает для этого эффективный встроенный модуль/модуль, сообщите мне об этом.

Размер полезной нагрузки тела можно рассчитать, вызвав _serialize_request, когда вы добавили нужное количество запросов к созданному экземпляру BatchHttpRequest.

См. также документацию по клиентской библиотеке Python API по созданию пакетных запросов.


person karolyi    schedule 22.06.2015    source источник
comment
Как запрос вычисляет размер полезной нагрузки?   -  person John La Rooy    schedule 22.06.2015
comment
Вы можете прочитать Как спросить.   -  person boardrider    schedule 22.06.2015
comment
@JohnLaRooy Я добавил это к вопросу.   -  person karolyi    schedule 22.06.2015
comment
Как вы думаете, почему вызов _serialize_request снова и снова будет более эффективным, чем повторение одного и того же элемента самостоятельно?   -  person John La Rooy    schedule 22.06.2015
comment
Я думаю, вы не понимаете проблемы. Чтобы рассчитать правильное количество элементов, которые должны быть помещены в пакет итеративным способом, вам необходимо: - создать элемент запроса со строкой в ​​​​списке - вызвать _serialize_request, чтобы проверить, не превышает ли он максимально допустимый размер . как вы, вероятно, видели, _serialize_request перебирает все запросы в пакетном объекте, чтобы скомпилировать полезную нагрузку тела в кодировке MIME. Таким образом, итеративное добавление еще одного объекта и получение полезной нагрузки — это огромные накладные расходы, которых я хотел бы избежать, отсюда и мой вопрос.   -  person karolyi    schedule 22.06.2015


Ответы (1)


Хорошо, кажется, я создал что-то, что решает эту проблему, вот набросок идеи на питоне:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import random
import string
import sys

MAX_LENGTH = 20
MAX_SIZE = 11111


def get_random():
    return ''.join([
        random.choice(string.ascii_letters) for i in range(
            random.randrange(10, 1000))])


def get_random_list():
    return [get_random() for i in range(random.randrange(50, 1000))]


def get_str_length(rnd_list, item_idx):
    return len(''.join(rnd_list[:item_idx]))

rnd_list = get_random_list()


def calculate_ideal_amount(rnd_list):
    list_bounds = {
        'first': 1,
        'last': len(rnd_list)
    }
    print ('ORIG_SIZE: %s, ORIG_LEN: %s' % (
        get_str_length(rnd_list, len(rnd_list)), len(rnd_list)))
    if get_str_length(rnd_list, list_bounds['first']) > MAX_SIZE:
        return 0
    if get_str_length(rnd_list, list_bounds['last']) <= MAX_SIZE and \
            list_bounds['last'] <= MAX_LENGTH:
        return list_bounds['last']
    while True:
        difference = round((list_bounds['last'] - list_bounds['first']) / 2)
        middle_item_idx = list_bounds['first'] + difference
        str_len = get_str_length(
            rnd_list, middle_item_idx)
        print(
            'MAX_SIZE: %s, list_bounds: %s, '
            'middle_item_idx: %s, diff: %s, str_len: %s,' % (
                MAX_SIZE, list_bounds, middle_item_idx, difference, str_len))
        # sys.stdin.readline()
        if str_len > MAX_SIZE:
            list_bounds['last'] = middle_item_idx
            continue
        if middle_item_idx > MAX_LENGTH:
            return MAX_LENGTH
        list_bounds['first'] = middle_item_idx
        if difference == 0:
            if get_str_length(rnd_list, list_bounds['last']) <= MAX_SIZE:
                if list_bounds['last'] > MAX_LENGTH:
                    return MAX_LENGTH
                return list_bounds['last']
            return list_bounds['first']

ideal_idx = calculate_ideal_amount(rnd_list)

print (
    len(rnd_list), get_str_length(rnd_list, len(rnd_list)),
    get_str_length(rnd_list, ideal_idx), ideal_idx,
    get_str_length(rnd_list, ideal_idx + 1))

Этот код делает то же самое, что я пытался описать, находя и изменяя границы списка, измеряя его возвращаемый (объединенный) размер, а затем возвращая индекс списка, где он должен быть нарезан для достижения наиболее эффективного размер строки. Этот метод позволяет избежать накладных расходов ЦП на компиляцию и измерение списка один за другим. Запуск этого кода покажет вам итерации, которые он делает в списке.

get_str_length, списки и другие функции можно заменить, чтобы использовать соответствующие функции в клиенте API, но это приблизительная идея.

Однако код не является надежным, решение должно быть чем-то вроде этого.

person karolyi    schedule 22.06.2015
comment
Я никогда не понимал, почему людям нравится брать язык, созданный для удобочитаемости, и превращать его в запутанный PHP. Я надеюсь, что этот код станет для вас понятным на следующей неделе. - person msw; 22.06.2015