https://docs.python.org/3/library/itertools.html
https://docs.python.org/3/library/itertools.html#itertools-recipes
https://realpython.com/python-itertools/
This module implements functions creating iterators for efficient looping.
The real power of 'itertools' lies in composing the functions to create fast, memory-efficient, and good-looking code.
import itertools
# Infinite iterators
# itertools.count(start=0, step=1])
itertools.count(10) # yield 10 11 12 13 14 ...
itertools.count(10, 5) # yield 10 15 20 25 ...
# enumerate(iterable, start=0) emulator without for loop
zip(itertools.count(start=0), iterable)
# itertools.cycle(iterable)
itertools.cycle('ABCD') # yield A B C D A B C D ...
itertools.cycle(range(1, 4)) # yield 1 2 3 1 2 3 1 2 3
# itertools.repeat(item [,n])
itertools.repeat(10) # yield 10 10 10 10 ...
itertools.repeat(10, 3) # yield 10 10 10 (3 times)
list(map(pow, range(10), itertools.repeat(2))) # works for any range (no for loop)
#list(map(pow, range(10), iter((lambda: 2), 1))) # the same
#list(pow(x, 2) for x in range(10)) # simple
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# itertools.chain(*iterables)
itertools.chain('ABC', 'DEF') # yield A B C D E F
# itertools.chain.from_iterable(iterable) # alternate constructor for chain()
itertools.chain.from_iterable(['ABC', 'DEF']) # yield A B C D E F
# itertools.batched(iterable, n) # Py3.12
list( itertools.batched('ABCDEFG', 3))
# [('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)]
# itertools.groupby(iterable[, key]) # sub-iterators grouped by value of key(v)
[k for k, g in groupby('AAAABBBCCDAABBB')] # a list of keys
# ['A', 'B', 'C', 'D', 'A', 'B']
[list(g) for k, g in groupby('AAAABBBCCD')] # a list of groups
# [['A', 'A', 'A', 'A'], ['B', 'B', 'B'], ['C', 'C'], ['D']]
# itertools.islice(iterable, stop)
# itertools.islice(iterable, start, stop [, step])
# If 'stop' is None, then iteration continues until the iterator is exhausted, if at all.
list(itertools.islice('ABCDEFG', 2)) # ['A', 'B'], stop=2
list(itertools.islice('ABCDEFG', 2, None)) # ['C', 'D', 'E', 'F', 'G']
list(itertools.islice('ABCDEFG', 0, None, 2)) # ['A', 'C', 'E', 'G']
# itertools.zip_longest(*iterables[, fillvalue]) in Py3
# itertools.izip_longest(*iterables[, fillvalue]) in Py2.6+
# It can be used for polynomials.
itertools.zip_longest('ABCD', 'xy', fillvalue='-')
# yield ('A', 'x'), ('B', 'y'), ('C', '-'), ('D', '-')
itertools.zip_longest([10, 20], [1, 2, 3, 4], fillvalue=0)
# yield (10, 1), (20, 2), (0, 3), (0, 4)
# itertools.product(*iterables, repeat=1)
# Cartesian product of input iterables.
# product(A, B) returns the same as ((x,y) for x in A for y in B).
list(itertools.product("abc", "123"))
# [('a', '1'), ('a', '2'), ('a', '3'),
# ('b', '1'), ('b', '2'), ('b', '3'),
# ('c', '1'), ('c', '2'), ('c', '3')]
[x+y for (x,y) in itertools.product("abc","123")]
# ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
[x+y for (x,y) in itertools.product("abc", repeat=2)]
# ['aa', 'ab', 'ac', 'ba', 'bb', 'bc', 'ca', 'cb', 'cc']
# Combinatoric generators. # itertools.permutations(iterable, r=None) # yield r-length tuples, all possible orderings, no repeated elements list(itertools.permutations([1, 2, 3])) # 3! = 6 perms # [(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)] list(itertools.permutations([1, 2, 3], 2)) # [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)] # itertools.combinations(iterable, r) # yield r-length tuples, in sorted order, no repeated elements list(itertools.combinations([1, 2, 3, 4], 3)) [(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)] # itertools.combinations_with_replacement(iterable, r) # yield r-length tuples, in sorted order, with repeated elements list(itertools.combinations_with_replacement([1, 2, 3, 4], 3)) # [(1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 1, 4), (1, 2, 2), (1, 2, 3), # (1, 2, 4), (1, 3, 3), (1, 3, 4), (1, 4, 4), (2, 2, 2), (2, 2, 3), # (2, 2, 4), (2, 3, 3), (2, 3, 4), (2, 4, 4), (3, 3, 3), (3, 3, 4), # (3, 4, 4), (4, 4, 4)]
# Given a list of values inputs and a positive integer n,
# write a function that splits inputs into groups of length n.
def naive_grouper(inputs, n):
num_groups = len(inputs) // n
return [tuple(inputs[i*n:(i+1)*n]) for i in range(num_groups)]
# all items from inputs are in the memory!
def better_grouper(inputs, n):
iters = [iter(inputs)] * n
# it is a list of n references to the same iterator
return zip(*iters)
# zip returns an iterator over tuples
def best_grouper(inputs, n, fillvalue=None):
iters = [iter(inputs)] * n
return itertools.zip_longest(*iters, fillvalue=fillvalue)
# it works even if n is not a factor of the length of inputs