Python · Синтаксис · Промежуточный
itertools
Освойте модуль itertools в Python: инструменты для работы с итераторами, генерации последовательностей и эффективного решения комбинаторных задач.
Краткое введение в тему и пояснения перед упражнениями (упражнения ниже):
Комбинаторика с itertools
#Справочник: itertools
#Упражнения:
Выровнять вложенный список
#Напишите функцию `flatten(nested)`, которая принимает список списков и возвращает плоский список с помощью `itertools.chain.from_iterable`. Пример: `flatten([[1, 2], [3], [4, 5, 6]])` → `[1, 2, 3, 4, 5, 6]`.
from itertools import chain
def flatten(nested):
pass
print(flatten([[1, 2], [3], [4, 5, 6]]))
# [1, 2, 3, 4, 5, 6]
Решение
from itertools import chain
def flatten(nested):
return list(chain.from_iterable(nested))
print(flatten([[1, 2], [3], [4, 5, 6]]))
# [1, 2, 3, 4, 5, 6]
Первые N из генератора
#Напишите функцию `first_n(gen, n)`, которая возвращает первые `n` элементов из любого генератора или итератора в виде списка, используя `itertools.islice`. Пример: `first_n((x**2 for x in range(100)), 5)` → `[0, 1, 4, 9, 16]`.
from itertools import islice
def first_n(gen, n):
pass
print(first_n((x**2 for x in range(100)), 5))
# [0, 1, 4, 9, 16]
Решение
from itertools import islice
def first_n(gen, n):
return list(islice(gen, n))
print(first_n((x**2 for x in range(100)), 5))
# [0, 1, 4, 9, 16]
Планировщик round-robin
#Напишите функцию `round_robin(tasks)`, которая принимает список имён задач и возвращает бесконечный цикличный итератор с помощью `itertools.cycle`. Затем используйте `itertools.islice` для получения первых 8 назначений. Пример: `list(islice(round_robin(["A", "B", "C"]), 8))` → `["A", "B", "C", "A", "B", "C", "A", "B"]`.
from itertools import cycle, islice
def round_robin(tasks):
pass
print(list(islice(round_robin(["A", "B", "C"]), 8)))
# ["A", "B", "C", "A", "B", "C", "A", "B"]
Решение
from itertools import cycle, islice
def round_robin(tasks):
return cycle(tasks)
print(list(islice(round_robin(["A", "B", "C"]), 8)))
# ["A", "B", "C", "A", "B", "C", "A", "B"]
Слить неравные последовательности
#Напишите функцию `merge_sequences(seq1, seq2, seq3, fill=None)`, которая объединяет три последовательности поэлементно с помощью `itertools.zip_longest`, используя `fill` как недостающее значение. Возвращает результат как список кортежей. Пример: `merge_sequences([1,2,3], ["a","b"], [True], fill=0)` → `[(1,"a",True), (2,"b",0), (3,0,0)]`.
from itertools import zip_longest
def merge_sequences(seq1, seq2, seq3, fill=None):
pass
print(merge_sequences([1, 2, 3], ["a", "b"], [True], fill=0))
# [(1, "a", True), (2, "b", 0), (3, 0, 0)]
Решение
from itertools import zip_longest
def merge_sequences(seq1, seq2, seq3, fill=None):
return list(zip_longest(seq1, seq2, seq3, fillvalue=fill))
print(merge_sequences([1, 2, 3], ["a", "b"], [True], fill=0))
# [(1, "a", True), (2, "b", 0), (3, 0, 0)]
Накопленный максимум
#Напишите функцию `running_max(numbers)`, которая возвращает список, где каждый элемент является максимальным значением, встреченным к текущему моменту в `numbers`. Используйте `itertools.accumulate`. Пример: `running_max([3, 1, 4, 1, 5, 9, 2, 6])` → `[3, 3, 4, 4, 5, 9, 9, 9]`.
from itertools import accumulate
import operator
def running_max(numbers):
pass
print(running_max([3, 1, 4, 1, 5, 9, 2, 6]))
# [3, 3, 4, 4, 5, 9, 9, 9]
Решение
from itertools import accumulate
import operator
def running_max(numbers):
return list(accumulate(numbers, func=max))
print(running_max([3, 1, 4, 1, 5, 9, 2, 6]))
# [3, 3, 4, 4, 5, 9, 9, 9]
Декартово произведение: сетка
#Напишите функцию `grid(rows, cols)`, которая возвращает все пары координат (row, col) для сетки заданных размеров, используя `itertools.product`. Пример: `grid(2, 3)` → `[(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)]`.
from itertools import product
def grid(rows, cols):
pass
print(grid(2, 3))
# [(0,0), (0,1), (0,2), (1,0), (1,1), (1,2)]
Решение
from itertools import product
def grid(rows, cols):
return list(product(range(rows), range(cols)))
print(grid(2, 3))
# [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)]
Все пары (комбинации)
#Напишите функцию `all_pairs(items)`, которая возвращает все уникальные неупорядоченные пары элементов (элемент не сочетается с собой). Используйте `itertools.combinations`. Пример: `all_pairs(["A", "B", "C", "D"])` → `[("A","B"), ("A","C"), ("A","D"), ("B","C"), ("B","D"), ("C","D")]`.
from itertools import combinations
def all_pairs(items):
pass
print(all_pairs(["A", "B", "C", "D"]))
# [("A","B"), ("A","C"), ("A","D"), ("B","C"), ("B","D"), ("C","D")]
Решение
from itertools import combinations
def all_pairs(items):
return list(combinations(items, 2))
print(all_pairs(["A", "B", "C", "D"]))
# [("A", "B"), ("A", "C"), ("A", "D"), ("B", "C"), ("B", "D"), ("C", "D")]
Группировка по категории
#Напишите функцию `group_by(items, key_func)`, которая группирует список элементов по функции ключа и возвращает словарь, отображающий каждый ключ на список элементов. Используйте `itertools.groupby` (не забудьте сначала отсортировать). Пример: с `items = ["apple", "ant", "banana", "bear", "cherry"]` и `key_func = lambda w: w[0]`, результат: `{"a": ["ant", "apple"], "b": ["banana", "bear"], "c": ["cherry"]}`.
from itertools import groupby
def group_by(items, key_func):
pass
words = ["apple", "ant", "banana", "bear", "cherry"]
print(group_by(words, lambda w: w[0]))
# {"a": ["ant", "apple"], "b": ["banana", "bear"], "c": ["cherry"]}
Решение
from itertools import groupby
def group_by(items, key_func):
sorted_items = sorted(items, key=key_func)
result = {}
for key, group in groupby(sorted_items, key=key_func):
result[key] = list(group)
return result
words = ["apple", "ant", "banana", "bear", "cherry"]
print(group_by(words, lambda w: w[0]))
# {"a": ["ant", "apple"], "b": ["banana", "bear"], "c": ["cherry"]}
takewhile: читать до разделителя
#Напишите функцию `read_until_empty(lines)`, которая принимает список строк и возвращает только строки до первой пустой строки (или строки содержащей только пробелы), используя `itertools.takewhile`. Пример: `read_until_empty(["hello", "world", "", "more", "text"])` → `["hello", "world"]`.
from itertools import takewhile
def read_until_empty(lines):
pass
print(read_until_empty(["hello", "world", "", "more", "text"]))
# ["hello", "world"]
Решение
from itertools import takewhile
def read_until_empty(lines):
return list(takewhile(lambda line: line.strip() != "", lines))
print(read_until_empty(["hello", "world", "", "more", "text"]))
# ["hello", "world"]
Генератор паролей
#Напишите функцию `generate_passwords(chars, length, count)`, которая генерирует `count` уникальных паролей длиной `length` символов, где каждый пароль строится из `itertools.product(chars, repeat=length)`. Возвращает первые `count` произведений как объединённые строки. Пример: `generate_passwords("ab", 2, 4)` → `["aa", "ab", "ba", "bb"]` (используйте product по порядку, без случайности).
from itertools import product, islice
def generate_passwords(chars, length, count):
pass
print(generate_passwords("ab", 2, 4))
# ["aa", "ab", "ba", "bb"]
Решение
from itertools import product, islice
def generate_passwords(chars, length, count):
return ["".join(p) for p in islice(product(chars, repeat=length), count)]
print(generate_passwords("ab", 2, 4))
# ["aa", "ab", "ba", "bb"]