10 Python Itertools, которые сделают ваш код аккуратнее и чище.
Красота Python заключается в его простоте.
Не только потому, что синтаксис Python элегантен, но и потому, что он имеет множество хорошо спроектированных встроенных модулей, которые помогают нам эффективно реализовывать общие функции.
Модуль itertools
, который является хорошим примером, предоставляет нам много мощных инструментов для управления итерируемыми объектами Python в более сокращённом коде.
Делайте больше меньшими средствами. Это то, что вы можете получить от модуля itertools
.
1. itertools.product(): хитрый способ избежать вложенных циклов
Когда программа становится всё более и более сложной, вам может понадобиться написать вложенные циклы. В то же время ваш код Python станет некрасивым и нечитаемым:
list_a = [1, 2020, 70]
list_b = [2, 4, 7, 2000]
list_c = [3, 70, 7]
for a in list_a:
for b in list_b:
for c in list_c:
if a + b + c == 2077:
print(a, b, c)
# 70 2000 7
Как снова сделать приведённый выше код привлекательным?
Функция itertools.product()
— ваш друг:
from itertools import product
list_a = [1, 2020, 70]
list_b = [2, 4, 7, 2000]
list_c = [3, 70, 7]
for a, b, c in product(list_a, list_b, list_c):
if a + b + c == 2077:
print(a, b, c)
# 70 2000 7
Как показано выше, она возвращает декартово произведение входных итераций, что помогает нам объединить 3 вложенных цикла for в один.
2. itertools.compress(): удобный способ фильтрации данных
Мы можем отфильтровать список элементов через один или несколько циклов без особых проблем.
Но иногда нам не нужно писать циклы, ведь существует функция, именуемая как itertools.compress()
.
Функция itertools.compress()
возвращает итератор, который фильтрует итерируемый объект на основе значений соответствующей логической маски.
Например, следующий код выбирает истинных лидеров с помощью функции itertools.compress()
:
import itertools
leaders = ['Yang', 'Elon', 'Tim', 'Tom', 'Mark']
selector = [1, 1, 0, 0, 0]
print(list(itertools.compress(leaders, selector)))
# ['Yang', 'Elon']
Второй аргумент selector
работает как маска, мы также можем определить его следующим образом:
selector = [True, True, False, False, False]
3. itertools.groupby(): группировка элементов итерируемого объекта
Функция itertools.groupby()
представляет собой удобный способ группировать смежные повторяющиеся элементы итерируемого объекта.
Например, мы можем сгруппировать длинную строку следующим образом:
from itertools import groupby
for key, group in groupby('YAaANNGGG'):
print(key, list(group))
# Y ['Y']
# A ['A']
# a ['a']
# A ['A']
# N ['N', 'N']
# G ['G', 'G', 'G']
Кроме того, мы можем использовать его второй аргумент, чтобы сообщить функции groupby()
, как определить, являются ли два элемента одинаковыми или нет:
from itertools import groupby
for key, group in groupby('YAaANNGGG', lambda x: x.upper()):
print(key, list(group))
# Y ['Y']
# A ['A', 'a', 'A']
# N ['N', 'N']
# G ['G', 'G', 'G']
4. itertools.combinations(): получение всех комбинаций заданной длины из итерируемого объекта.
Новичку нужно некоторое время, чтобы написать безошибочную функцию для получения всех возможных комбинаций списка.
На самом деле, если он знает функцию itertools.combination()
, он может легко найти решение:
import itertools
author = ['Y', 'a', 'n', 'g']
result = itertools.combinations(author, 2)
for x in result:
print(x)
# ('Y', 'a')
# ('Y', 'n')
# ('Y', 'g')
# ('a', 'n')
# ('a', 'g')
# ('n', 'g')
Как показано в приведённой выше программе, функция itertools.combination()
имеет два параметра: один — исходный итерируемый объект, а другой — длина подпоследовательностей, которые будут сгенерированы функцией.
5. itertools.permutations(): получить все перестановки заданной длины из итерируемого объекта.
Поскольку есть функция для получения всех комбинаций, конечно, есть ещё одна функция, названная itertools.permutations
, для получения всех возможных перестановок:
import itertools
author = ['Y', 'a', 'n', 'g']
result = itertools.permutations(author, 2)
for x in result:
print(x)
# ('Y', 'a')
# ('Y', 'n')
# ('Y', 'g')
# ('a', 'Y')
# ('a', 'n')
# ('a', 'g')
# ('n', 'Y')
# ('n', 'a')
# ('n', 'g')
# ('g', 'Y')
# ('g', 'a')
# ('g', 'n')
Как показано выше, использование функции itertools.permutations()
аналогично itertools.combinations()
. Разница только в их результатах.
6. itertools.accumulate(): создание накопленных элементов из итерируемого объекта.
Получение серии накопленных значений на основе итерируемого объекта является распространённым требованием. С помощью функции itertools.accumulate()
нам не нужно писать никаких циклов для реализации.
import itertools
import operator
nums = [1, 2, 3, 4, 5]
print(list(itertools.accumulate(nums, operator.mul)))
# [1, 2, 6, 24, 120]
Вышеупомянутая программа будет такой, как показано ниже, если мы не хотим использовать operator.mul
:
import itertools
nums = [1, 2, 3, 4, 5]
print(list(itertools.accumulate(nums, lambda a, b: a * b)))
# [1, 2, 6, 24, 120]
7. itertools.repeat(), itertools.cycle(), itertools.count(): создание бесконечных итерируемых объектов
В некоторых случаях нам нужно получить бесконечную итерацию. Есть 3 полезные функции:
itertools.repeat(): многократная генерация одного и того же элемента
Например, мы можем получить три одинаковых «Yang» следующим образом:
import itertools
print(list(itertools.repeat('Yang', 3)))
# ['Yang', 'Yang', 'Yang']
itertools.cycle(): получение бесконечного итератора, зацикливание
Функция itertools.cycle
не остановится, пока вы не разорвёте цикл:
import itertools
count = 0
for c in itertools.cycle('Yang'):
if count >= 12:
break
else:
print(c, end=',')
count += 1
# Y,a,n,g,Y,a,n,g,Y,a,n,g,
itertools.count(): генерация бесконечной последовательности чисел
Если всё, что нам нужно, это числа, используйте функцию itertools.count
:
import itertools
for i in itertools.count(0, 2):
if i == 20:
break
else:
print(i, end=" ")
# 0 2 4 6 8 10 12 14 16 18
Как показано выше, её первый параметр — это начальный номер, а второй параметр — шаг.
8. itertools.pairwise(): лёгкое получение кортежа пар
Начиная с Python 3.10, модуль itertools
имеет новую функцию с именем pairwise
. Это небольшой и аккуратный инструмент для создания последовательных перекрывающихся пар из итерируемого объекта.
import itertools
letters = ['a', 'b', 'c', 'd', 'e']
result = itertools.pairwise(letters)
print(list(result))
# [('a', 'b'), ('b', 'c'), ('c', 'd'), ('d', 'e')]
9. itertools.takewhile(): фильтрация элементов
itertools.takewhile()
возвращает итератор, который генерирует элементы из итерируемого объекта, пока данная функция предиката оценивается как True
.
import itertools
nums = [1, 61, 7, 9, 2077]
print(list(itertools.takewhile(lambda x: x < 100, nums)))
# [1, 61, 7, 9]
Эта функция отличается от встроенной функции
filter()
.
Функция filter
будет проходить по всему списку:
nums = [1, 61, 7, 9, 2077]
print(list(filter(lambda x: x < 10, nums)))
# [1, 7, 9]
Однако функция itertools.takewhile
, как следует из её названия, остановится, когда вычисляющая функция будет равна False
:
import itertools
nums = [1, 61, 7, 9, 2077]
print(list(itertools.takewhile(lambda x: x < 10, nums)))
# [1]
10. itertools.dropwhile(): обратная операция itertools.takewhile()
Эта функция кажется обратной идеей предыдущей.
Функция itertools.takewhile()
возвращает элементы итерируемого объекта до тех пор, пока функция предиката имеет значение True
, тогда как itertools.dropwhile()
отбрасывает элементы итерируемого объекта до тех пор, пока функция предиката имеет значение True
, а затем возвращает оставшиеся элементы.
import itertools
nums = [1, 61, 7, 9, 2077]
print(list(itertools.dropwhile(lambda x: x < 100, nums)))
# [2077]
Спасибо за то, что дочитали эту статью до конца!