На этом шаге мы рассмотрим решение этой задачи.
У вас есть вложенная последовательность, и вы хотите превратить ее в один плоский список значений.
Это легко решается с помощью рекурсивного генератора с инструкцией yield from. Например:
>>> from collections.abc import Iterable >>> def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): yield from flatten(x) else: yield x >>> items = [1, 2, [3, 4, [5, 6], 7], 8] >>> # Выводит 1 2 3 4 5 6 7 8 >>> for x in flatten(items): print(x) 1 2 3 4 5 6 7 8 >>>
В этой программе isinstance(x, Iterable) просто проверяет, является ли элемент итерируемым объектом. Если это так, то yield from используется в качестве некой подпрограммы, чтобы выдать все его значения. Конечный результат - одна последовательность без вложенности.
Дополнительный аргумент ignore_types и проверка not isinstance(x, ignore_types) нужны для предотвращения определения строк и байтов как итерируемых последовательностей, разбиения их на отдельные символы. Это позволяет вложенным спискам строк работать так, как большинство людей этого и ожидает:
>>> items = ['Dave', 'Paula', ['Thomas', 'Lewis']] >>> for x in flatten(items): print(x) Dave Paula Thomas Lewis >>>
Инструкция yield from - отличный способ написания генераторов, которые вызывают другие генераторы в качестве подпроцедур. Без использования этой инструкции вам придется вставить в код дополнительный цикл. Например:
def flatten(items, ignore_types=(str, bytes)): for x in items: if isinstance(x, Iterable) and not isinstance(x, ignore_types): for i in flatten(x): yield i else: yield x
Хотя это незначительное изменение, инструкция yield from просто приятнее и делает код чище.
Как было отмечено, дополнительная проверка на строки и байты нужна для предотвращения их разбивки на отдельные символы. Если есть еще какие-то типы, которые вы не хотите раскрывать, вы просто можете передать другие значения в ignoretypes.
Стоит отметить, что yield from играет более важную роль в продвинутых программах, использующих корутины (сопрограммы) и основанную на генераторах многопоточность.
На следующем шаге мы рассмотрим последовательное итерирование по слитым отсортированным итерируемым объектам.