Как вывести один элемент из буфера из множества элементов и периодически пополнять буфер? - PullRequest
1 голос
/ 09 марта 2019

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

Вот некоторый псевдокод:

def get_data_chunk(datasource, chunksize=10):
    # grab chunksize elements of datasource
    return data_chunk # a list of dict, usually

def generator(datasource):
    data_chunk = get_data_chunk(datasource)
    for item in data_chunk:
        yield item
    # if no more item in data_chunk, reload from get_data_chunk
    # until datasource does not return anything

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

1 Ответ

2 голосов
/ 09 марта 2019

У вас есть два варианта:

  1. Либо используйте цикл while True: и выходите, когда следующий блок пуст:

    def generator(datasource):
        while True:
            data_chunk = get_data_chunk(datasource)
            if not data_chunk:
                return
            for item in data_chunk:
                yield item
    
  2. Используйте форму с двумя аргументами функции iter() в цикле for:

    def generator(datasource):
        for data_chunk in iter(lambda: get_data_chunk(datasource), None):
            for item in data_chunk:
                yield item
    

    или, используя itertools.chain.from_iterable():

    from itertools import chain
    
    def generator(datasource):
        chunk_iter = iter(lambda: get_data_chunk(datasource), None)
        yield from chain.from_iterable(chunk_iter)
    

Последнее требует, чтобы вы знали, как выглядит «конечное значение». Выше я предполагал, что это конечное значение равно None, но если это пустой список, вам нужно заменить None на [].

Демо-версия:

>>> from itertools import chain, islice
>>> from random import randrange
>>> demosource = (randrange(11, 81) for _ in range(17))
>>> def get_data_chunk(datasource, chunksize=10):
...     return list(islice(datasource, chunksize))
...
>>> def generator(datasource):
...     chunk_iter = iter(lambda: get_data_chunk(datasource), [])  # last chuck is an empty list
...     yield from chain.from_iterable(chunk_iter)
...
>>> list(generator(demosource))
[38, 47, 74, 13, 23, 24, 47, 61, 30, 38, 70, 41, 43, 47, 37, 34, 67]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...