The itertools module
itertools builds lazy iterators that combine like pipes. Nothing is computed until something loops over the result.
from itertools import (
accumulate, batched, chain,
groupby, islice, pairwise)
print(list(chain([1, 2], [3])))
print(list(accumulate([1, 2, 3])))
print(list(pairwise("abc")))
print(list(batched(range(5), 2)))
print(list(islice("abcdef", 1, 4)))
for key, grp in groupby("aabccc"):
print(key, len(list(grp)))chainjoins iterables, andisliceslices any iterator, even an endless one such ascount().accumulateyields running totals, or running results of any two-argument function, such asmax.pairwiseyields neighbouring pairs, andbatchedyields tuples of n items with a shorter last one.groupbygroups only consecutive items with equal keys. Sort by the same key first if you want one group per key.product,permutationsandcombinationsreplace nested loops over choices.
These functions accept any iterable and never need len or indexing, so they also work on generators and files.
Your task
Using itertools, write runs(text) returning (char, count) pairs for each run of repeated characters, increases(nums) counting the neighbouring pairs where the second number is larger, peaks(nums) returning the running maximum as a list, and pairings(names) returning every unordered pair as a list of tuples in input order. Each must accept any iterable.
The explanation opens here once every check passes.