Мастерство стандартной библиотеки: itertools.chain
Представьте, что вам нужно пробежать за один проход по N коллекциям, например по двум вот таким.
In [2]: l1 = list(range(5))
In [3]: l2 = list(range(10))
In [4]: l1
Out[4]: [0, 1, 2, 3, 4]
In [5]: l2
Out[5]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Естественно, возникает желание написать вот такой цикл:
for i in l1+l2: print(i, end=", ")
# 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
Проблема с этим решением в том, что вы создаете новый лист, и его длина равна сумме длин исходных листов. Если вы знаете, что они оба маленькие, то это, скорее всего, ничего, но представьте, что каждый весит в памяти по 1 Гб. В пике ваша программа съест в 2 раза больше памяти, чем весят ваши исходные листы. И легко может вообще не выполниться, если на машине не хватит RAM.
На самом деле, мы хотим сделать что-то вроде такого:
def gen(l1, l2):
yield from l1
yield from l2
for i in gen(l1,l2): print(i, end=", ")
# 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
То есть не делать новых листов и ничего не копировать - а пробежать по ним по очереди, но через один итератор.
И это именно то, что делает itertools.chain
import itertools
for i in itertools.chain(l1,l2): print(i, end=", ")
# 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
У чейна есть вторая форма, itertools.chain.from_iterable
. Это абсолютно тоже самое, но с немного другим интерфейсом.
for i in itertools.chain.from_iterable([l1, l2]): print(i, end=", ")
# 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
То есть, в общем виде:
# это itertools.chain
In [5]: def my_chain(*collections):
...: for collection in collections:
...: yield from collection
# это itertools.chain.from_iterable
In [8]: def my_chain_from_iterable(collections):
...: for collection in collections:
...: yield from collection
Стоило ли делать 2 чейна, отличающихся на анпэкинг аргументов? Не знаю, не мне судить авторов itertools, они боги. Но я знаю, что нет нужды плодить сущности без необходимости. А это возвращает нас к теме создания лишних новых листов и вообще лишних объектов.
Это я к чему?
Берегите ресурсы. Кажется, все уже давно не так и в наш век ресурсов сколько угодно, но после первого инвойса из Google Cloud Functions (где вы платите за использованные циклы CPU и мегабайто-часы съеденной памяти) уверен, Вы передумаете. Да и сам по себе питон не слишком экономичен - не стоит ему еще дополнительно помогать.
Что еще почитать?
Наш телеграм канал - так Вы никогда не пропустите очередную порцию философских размышлений на полчаса над 5 строчками кода.