Как объединить два генератора в Python: использование os.walk

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Если вам требуется соединить два или более итерируемых объекта в Python в один поток данных, то функция itertools.chain() будет идеальным решением. Она успешно справляется как с генераторами, так и с другими типами итерируемых объектов.

Рассмотрим следующий пример:

Python
Скопировать код
from itertools import chain

# Допустим, gen_numbers — это пицца, а gen_letters — пиво
gen_numbers = (pizza for pizza in range(3))  # 🍕
gen_letters = (beer for beer in 'ab')  # 🍺

# Смешиваем эти удовольствия с помощью chain
combined = chain(gen_numbers, gen_letters)

# Пора наслаждаться! Едим и пьём
for item in combined:
    print(item)  # Выведет: 🍕 и 🍺

Отметьте, что chain() не расходует сразу все пиццы и пиво — элементы потребляются постепенно, как в ленивое воскресенье. [AsideBanner]

Делегирование подгенераторам с помощью yield from

В Python версии 3.3 был введён удобный синтаксис yield from, позволяющий легко передать выполнение операций одного генератора другому. Со всплеском Python 3.5+, его эффективность стала особенно заметна в задачах объединения нескольких генераторов.

Python
Скопировать код
def combined_generators(gen1, gen2):
    yield from gen1  # Занимаюсь первой партией
    yield from gen2  # Теперь твоя очередь

# Пример использования
for item in combined_generators(gen_numbers, gen_letters):
    print(item)  # Результат такой же, как в ленивое воскресенье

В этом случае мы избегаем прямого соединения и используем метод делегирования.

Краткость и ясность с помощью генераторных выражений

Если вам ближе использование встроенных возможностей и chain() по каким-то причинам вам не подходит, обратите внимание на генераторное выражение. Оно позволяет соединить циклы в единое целое, буквально в одну строку.

Вот краткий пример:

Python
Скопировать код
combined = (item for iterable in (gen_numbers, gen_letters) for item in iterable)

# С радостью поделимся результатом!
for item in combined:
    print(item)

Этот метод незаменим в сложных сценариях с вложенными циклами или при необходимости проведения пользовательских проверок.

Ловушки, которых следует избегать

Ошибкой было бы попытаться "сложить" генераторы с помощью +. Также стоит беречь генераторы от преждевременного преобразования в списки. Не забывайте, их суть заключается в ленивой генерации элементов. Не стоит раскрывать все карты заранее — никому не вкусно недопечённая пицца!

Визуализация

Представьте два ручейка, объединяющихся в бурную реку:

Markdown
Скопировать код
Ручей 1 (🏞️): [🌱, 🌿, 🍃]
Ручей 2 (🌊): [🐟, 🦈, 🐠]

Теперь присоединим их:

Python
Скопировать код
combined_stream = (item for stream in (meadow_stream, ocean_stream) for item in stream)

И вот перед вами это чудо:

Markdown
Скопировать код
🏞️🔀🌊: [🌱, 🌿, 🍃, 🐟, 🦈, 🐠]
# Единая, гармоничная экосистема!

Ваши генераторы идеально сочитаются друг с другом!

Глубже: Обработка сложных структур данных

Если вам требуется соединить сложные структуры данных, например директории или файлы, Python предлагает функцию itertools.chain.from_iterable(). Её использование особенно полезно, если вы работаете с структурами папок.

Посмотрим на пример:

Python
Скопировать код
from itertools import chain

# Допустим, что мы объединяем файлы из различных директорий
files_folder1 = ['file1.txt', 'file2.doc']  # Папка 1
files_folder2 = ['file3.pdf', 'file4.ppt']  # Папка 2

# Объединяем все файлы в одну коллекцию
all_files = chain.from_iterable([files_folder1, files_folder2])

# Просматриваем результаты
for file in all_files:
    print(file)  # Выведет 'file1.txt', 'file2.doc', 'file3.pdf', 'file4.ppt'

Справляясь со сложностями итераций

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

Python
Скопировать код
def filtered_data(gen, filter_value):
    for data in gen:
        if data > filter_value:
            yield data  # Возвращаем данные, которые превышают порог фильтрации

# Пример использования
for item in filtered_data(gen_numbers, 1):
    print(item)  # Выведет 2 и 3, если генераторы не были исчерпаны ранее

Полезные материалы

  1. Python itertools.chain() – Официальная документация
  2. PEP 380 – Синтаксис для делегирования подгенератору
  3. Библиотека More Itertools – Предлагает chain.from_iterable()
  4. Объединение нескольких генераторов – Stack Overflow
  5. Сложные выражения в Python – Расширенное руководство
  6. Понимание звёздочки(*) в Python – Использование для распаковки
  7. Введение в декораторы Python – Real Python