# Itertools in Python

Translation of the article was prepared on the eve of the start of the course “Python Developer. Basic”

Module `itertools` standardizes a core set of fast memory efficient tools that are useful on their own or in conjunction with other tools. Together they form an “iterator algebra” that allows you to concisely and efficiently create custom tools in pure Python.

The itertools module is in the Python standard library.

The module provides the following types of iterators:

• Endless iterators;

• Final iterators;

• Combinatorial generators.

The returned object will also be an iterator. We can iterate over the iterator with:

• function `«next»`

• cycle `for`

• converting to a list using `list()`

Infinite iterators:

`count()`, `cycle()`, `repeat()`

1. `count()`

Creates an iterator that returns evenly spaced values ​​starting at the number specified in the argument start… Default start is equal to 0 and step -1. The step can also be a non-integer value. This function will return an infinite iterator.

``````import itertools
c=itertools.count()
#next() function returns next item in the iterator.By default starts with number 0 and increments by 1.
print (next(c))#Output:0
print (next(c))#Output:1
print (next(c))#Output:2
print (next(c))#Output:3

#Returns an infinite iterator starting with number 5 and incremented by 10. The values in the iterator are accessed by next()
c1=itertools.count(5,10)
print(next(c1))#Output:5
print(next(c1))#Output:10
print(next(c1))#Output:15

#accessing values in the iterator using for loop.step argument can be float values also.
c2=itertools.count(2.5,2.5)
for i in c2:
#including terminating condition, else loop will keep on going.(infinite loop)
if i>25:
break
else:
print (i,end=" ") #Output:2.5 5.0 7.5 10.0 12.5 15.0 17.5 20.0 22.5 25.0

#step can be negative numbers also.negative numbers count backwards.
c3=itertools.count(2,-2.5)
print (next(c3))#Output:2
print (next(c3))#Output:-0.5
print (next(c3))#Output:-3.0``````

It is also used as an argument in functions `map()` and `zip()`

`zip()` Is a built-in Python function that allows you to combine matching elements from multiple sequences and return an object zip, which is a tuple iterator. `itertools.count()` can be used as input sequence for function `zip()`

``````import itertools
#itertools.count() used in zip()
l1=[5,15,25]
l2=zip(itertools.count(),l1)
#It will return zip object which is an iterable instance of zip class
print (l2)#Output:<zip object at 0x032C92C8>

#we can convert zip object to list.
print (list(l2))#Output:[(0, 5), (1, 15), (2, 25)]

# We can access the zip object by using "for loop".This is more efficient way to access large sequences.It won't be memory intensive.
l3=zip(itertools.count(),l1)
for i in l3:
print (i)
'''
Output:
(0, 5)
(1, 15)
(2, 25)
'''``````

`map()` used to apply a function to all elements in the specified iterator and return an object mapwhich is also an iterator. We can use `itertools.count()` as an argument to the function `map()`

``````import itertools
#lambda function and itertools.count() is given as argument in map()function.
l1=map(lambda x:x**2,itertools.count())
#It returns a map object which is an iterator.
print(l1)#Output:<map object at 0x00E2E610>

#iterate thorugh map object using for loop
for i in l1:
if i>50:
break
else:
print (i ,end=" ")#Output:0 1 4 9 16 25 36 49``````

2. `cycle()`

Creates an iterator that returns the elements from the iterable and stores a copy of each. When the iterable ends, the item from the saved copy is returned. Works endlessly.

`itertools.cycle(iterable)`

``````import itertools
l1=[1,2,3]
#using list iterable as an argument in itertools.cycle()
l2=itertools.cycle(l1)
print (l2)#Output:<itertools.cycle object at 0x02F794E8>

count=0
for i in l2:
#It will end in infinite loop. So have to define terminating condition.
if count > 15:
break
else:
print (i,end=" ")#Output:1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1
count+=1

#string is given as an argument in itertools.cycle() function.
s1="hello"
l3=itertools.cycle(s1)
#accessing the iterator using next()
print (next(l3))#Output:h
print (next(l3))#Output:e
print (next(l3))#Output:l
print (next(l3))#Output:l
print (next(l3))#Output:o``````

3. `repeat()`:

Creates an iterator that returns an object over and over. Executes indefinitely if no argument value is specified `times`

Can be used as an argument in functions `map()` and `zip()`

`itertools.repeat(object,times)`

``````import itertools
#times argument is not mentioned. It will result in infinite loop.
l1=itertools.repeat(2)
print (next(l1))#Output:2
print (next(l1))#Output:2

#string is used as an argument.times argument is mentioned as 10.It will repeat the string 10 times.
l2=itertools.repeat("hello",times=10)
for i in l2:
print (i,end=" ")#Output:hello hello hello hello hello hello hello hello hello hello
print (" ")

#list is used as argument
l3=itertools.repeat([1,2,3],times=3)
for i in l3:
print (i,end=" ")#Output:[1, 2, 3] [1, 2, 3] [1, 2, 3]
print (" ")

#tuple is used as an argument
l4=itertools.repeat(('red','blue'),times=3)
for i in l4:
print (i,end=" ")#Output:('red', 'blue') ('red', 'blue') ('red', 'blue')``````

Here function is used as function argument `map()`

``````import itertools
#It will return square of numbers from 0 to 9.
l1=map(pow,range(10),itertools.repeat(2))
print(l1)#Output:<map object at 0x011CEC10>

#iterate through map object using for loop
for i in l1:
print (i,end=" ") #Output:0 1 4 9 16 25 36 49 64 81``````

Here the function is used as an argument in the function `zip()`

``````import itertools
#itertools.repeat() used in zip()
l1=[5,15,25]
l2=zip(itertools.repeat(2),l1)
#It will return zip object which is an iterable instance of zip class
print (l2)#Output:<zip object at 0x032794E8>

#we can convert zip object to list.
print (list(l2))#Output:[(2, 5), (2, 15), (2, 25)]

# We can access zip object by using for loop.This is more efficient way to access large sequences.It won't be memory intensive.
l3=zip(itertools.repeat("hello"),l1)
for i in l3:
print (i)
'''
Output:
('hello', 5)
('hello', 15)
('hello', 25)
'''``````

Final iterators:

1`accumulate()`:

Creates an iterator that returns the accumulated sum or accumulated result of other binary functions that are specified in the parameter func

Typically, the number of items in the output iteration is the same as the number of items in the input iteration. If the parameter is specified initial, it is also counted, so the output iteration contains one more element than the input.

`functools.reduce()` only returns the final accumulated value for a similar function.

Parameter` initial` added to Python version 3.8.

`itertools.accumulate(iterable,func,*,initial=None)`

``````import itertools
#using add and mul operator,so importing operator module
import operator
#using reduce(),so importing reduce() from functools module
from functools import reduce

#If func parameter is not mentioned,by default it will perform addition operation)
l1=itertools.accumulate([1,2,3,4,5])
print (l1)#Output:<itertools.accumulate object at 0x02CD94C8>
#Converting iterator to list object.
print (list(l1))#Output:[1, 3, 6, 10, 15]
#using reduce() for same function
print (r1)#Output:15

#If initial parameter is mentioned, it will start accumulating from the initial value.
#It will contain more than one element in the ouptut iterable.
print (list(l2))#Output:[10, 11, 13, 16, 20, 25]

#it takes operator mul as function
# It will perform multiplication on first two elements, then it will perform multiplication of next two element in the iterable.
l3=itertools.accumulate([1,2,3,5,5],operator.mul)
print (list(l3))#Output:[1, 2, 6, 30, 150]
#using reduce() for same function mul.
r2=reduce(operator.mul,[1,2,3,4,5])
print (r2)#Output:120

l4=itertools.accumulate([2,4,6,3,1],max)
print (list(l4))#Output:[2, 4, 6, 6, 6]
#using reduce for same function max
r3=reduce(max,[2,4,6,3,1])
print (r3)#Output:6

#It takes min function as parameter.
# It will compare two elements and find the minimum element,then compare the other elements and find the mininum element.
l5=itertools.accumulate([2,4,6,3,1],min)
print (list(l5))#Output:[2, 2, 2, 2, 1]
#using reduce() for same function min
r4=reduce(min,[2,4,6,3,1])
print (r4)#Output:1``````

2. `chain()`:

Creates an iterator that returns an item from the iterable until it ends and then moves on to the next. He will consider the sequences following each other as one.

`itertools.chain(*iterables)`

``````import itertools
l1=itertools.chain(["red","blue"],[1,2,3],"hello")
#Returns an iterator object
print (l1)#Output:<itertools.chain object at 0x029FE4D8>
#converting iterator object to list object
print(list(l1))#Output:['red', 'blue', 1, 2, 3, 'h', 'e', 'l', 'l', 'o']

l2=itertools.chain("ABC","DEF","GHI")
print (list(l2))#Output:['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']``````

3`chain.from_iterable()`

This function takes one iterable as an input argument and returns a “glued” iterable containing all the elements of the input. All elements supplied to the input must be iterable, otherwise a TypeError exception will be thrown.

`chain.from_iterable(iterable)`

``````import itertools
l1=itertools.chain.from_iterable(["ABC","DEF","GHI"])
print (list(l1))#Output:['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']

l2=itertools.chain(["ABC","DEF","GHI"])
print (list(l2))#Output:['ABC', 'DEF', 'GHI']

#all elements in the input iterable should be iterable.Otherwise it will raise TypeError.
l3=itertools.chain.from_iterable([1,2,3])
print (list(l3))#Output:TypeError: 'int' object is not iterable``````

4`compress()`

Creates an iterator that filters items data, returning only those that contain the matching element in the selectors (selectors) in True. Terminates execution when either data or selectors have run out.

`itertools.compress(data,selectors)`

``````import itertools
selectors=[True,False,True,False]
l1=itertools.compress([1,2,3,4],selectors)
#Only returns element whose corresponding selector is True.
print (list(l1))#Output:[1,3]

#filter - instead of passing an iterable of True and False. function is used to determine the value "True or False"
l2=filter(lambda x:x%2!=0,[1,2,3,4])
print (list(l2))#Output:[1,3]``````

five. `dropwhile()`

Creates an iterator that discards elements from an iterable until a predicate (predicate) is True and then returns each item. The iterator will not return output until the predicate is False.

`itertools.dropwhile(predicate,iterable)`

6`takewhile()`

Creates an iterator that returns elements from the iterable as long as the predicate is True.

`itertools.takewhile(predicate,iterable)`

``````import itertools
##iterator will start displaying after condition is False.
l1=itertools.dropwhile(lambda x:x>4,[5,6,7,8,9,1,2,3])
print (l1)#Output:<itertools.dropwhile object at 0x02E592C8>
print (list(l1))#Output:[1, 2, 3]
#iterator used to print values till condition is False.
l4=itertools.takewhile(lambda x:x>4,[5,6,7,8,9,1,2,3])
print (list(l4))#Output:[5, 6, 7, 8, 9]

#iterator will start displaying after condition is False.
l2=itertools.dropwhile(lambda x:x%2==0,[2,4,6,8,10])
print (list(l2))#Output:[]
#iterator used to print values till condition is False.
l3=itertools.takewhile(lambda x:x%2==0,[2,4,6,8,10])
print (list(l3))#Output:[2, 4, 6, 8, 10]``````

7`filterfalse()`:

Creates an iterator that filters the elements of the iterable, returning only those for which the predicate is False. If the predicate is None, it returns elements that are False.

Method `filter()` returns an iterator that contains the elements of the iterable for which the function returns True.

``itertools.filterfalse(predicate,iterables)``
``````import itertools
#iterator will filter the elements from the iterable which returns False for the given function
l1=itertools.filterfalse(lambda x:x>4,[1,2,3,4,5,6,7,8,9])
print (l1)#Output:<itertools.filterfalse object at 0x0083E658>
print (list(l1))#Output:[1, 2, 3,4]
#filter() function will  filter the elements from the iterable which returns True for the given function
l4=filter(lambda x:x>4,[1,2,3,4,5,6,7,8,9])
print (list(l4))#Output:[5, 6, 7, 8, 9]

#iterator will filter the elements from the iterable which returns False for the given function
l2=itertools.filterfalse(lambda x:x%2==0,[2,4,6,8,10])
print (list(l2))#Output:[]
#filter() function will filter the elements from the iterable which returns True for the given function
l3=filter(lambda x:x%2==0,[2,4,6,8,10])
print (list(l3))#Output:[2, 4, 6, 8, 10]

#If predicate is None, returns the items that are False.
l5=itertools.filterfalse(None,[0,1,2,3,4,5])
print (list(l5))#Output:
#If predicate is None,filter() function returns the items that are True.
l6=filter(None,[0,1,2,3,4])
print (list(l6))#Output:[1, 2, 3, 4]``````

eight`zip_longest()`

Creates an iterator that aggregates elements from each iterable. If the iterators are of uneven length, then the missing values ​​are replaced by fillvalue… The iteration will continue until the longest iterable object ends.

IN `zip()` iteration continues until the shortest iterable object ends.

`itertools.zip_longest(*iterables,fillvalue=None)`

``````import itertools
#fillvalue is not given,by default it will be None.
#iteration continues until longest iterable is exhausted.
z1=itertools.zip_longest([1,2,3,4,5],['a','b','c'])
print (z1)#Output:<itertools.zip_longest object at 0x00AA3BE0>
#we can iterate through zip object using for loop or we can convert to list object.
print (list(z1))#Output:[(1, 'a'), (2, 'b'), (3, 'c'), (4, None), (5, None)]

#fillvalue is mentioned
z2=itertools.zip_longest([1,2,3,4,5],['a','b','c'],fillvalue="z")
print (list(z2))#Output:[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'z'), (5, 'z')]

#using zip(),iteration continues until shorest iterable is exhausted.
z3=zip([1,2,3,4,5],['a','b','c'])
print (list(z3))#Output:[(1, 'a'), (2, 'b'), (3, 'c')]``````

nine`starmap()`

Creates an iterator that evaluates a function by taking arguments from the iterable. Used instead of map ()when the argument parameters are already grouped into tuples in the same iterable (the data has been precompressed).

`itertools.starmap(function,iterable)`

``````import itertools
l1=itertools.starmap(pow,[(0,2),(1,2),(2,2)])
print (l1)#Output:<itertools.starmap object at 0x00C8E4D8>
#We can iterate through starmap object,using for loop or using next() function. We can also convert to list object.
print (list(l1))#Output:[0, 1, 4]

#using map()
l2=map(pow,[0,1,2],[2,2,2])
print (list(l2))#Output:[0, 1, 4]

a1=map(lambda x:x**2,[1,2,3])
print (list(a1))#Output:[1, 4, 9]

#If elements inside the iterable are not iterable, it will raise TypeError.
a2=itertools.starmap(lambda x:x**2,[1,2,3])
print (list(a2))#Output:TypeError: 'int' object is not iterable

a3=itertools.starmap(lambda x,y:x+y,[(0,1),(1,2),(2,3)])
print (list(a3))#Output:[1, 3, 5]``````

ten`islice()`

Creates an iterator that returns the selected items from an iterable. If a start is None, then the iteration starts at zero. If a step is None, then by default it is given the value 1. If stop is None, then the iteration will continue until the items in the iterable object run out, if they can run out at all. Otherwise, the iterator will stop at a specific position. IN `islice()` negative values ​​for parameters are not supported start, stop and step

`itertools.islice(iterable,stop)`

`itertools.islice(iterable, start, stop[, step])`

``````import itertools
#if only one argument is mentioned other than iterable,it is stop value.
l1=itertools.islice([1,2,3,4,5,6,7,8,9,10],5)
print (list(l1))#Output:[1, 2, 3, 4, 5]

#start=2 and stop=5 mentioned. It will start from second index and ends at fifth index
l2=itertools.islice([1,2,3,4,5,6,7,8,9,10],2,5)
print (list(l2))#Output:[3,4,5]

#start=2,step=3.It will start from second index and increment by 3.
l3=itertools.islice([1,2,3,4,5,6,7,8,9,10],2,None,3)
print (list(l3))#Output:[3,6,9]

#step is given as negative value,it raises ValueError
l4=itertools.islice([1,2,3,4,5,6,7,8,9,10],2,None,-2)
print (list(l4))#Output:ValueError: Step for islice() must be a positive integer or None.``````

eleven`tee()`

Returns n independent iterators from a single iterable.

`itertools.tee(iterable,n=2)`

``````import itertools
l1=[1,2,3,4,5]
l2=itertools.tee(l1,3)
print (l2)#Output:(<itertools._tee object at 0x028794C8>, <itertools._tee object at 0x028792C8>, <itertools._tee object at 0x02879528>)
for i in l2:
print (list(i))
'''
Output:
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
'''``````

12`groupby()`:

Creates an iterator that sequentially returns keys and groups from an iterable.

key Is a function that calculates the default key value for each item. If no key is specified or is None, then by default the key is an identity function that returns the item unchanged.

`itertools.groupby(iterable,key=None)`

``````import itertools
l1=[('color','red'),('color','blue'),('color','green'),('Numbers',1),('Numbers',5)]
l2=itertools.groupby(l1,key=lambda x:x)
print (l2)#Output:<itertools.groupby object at 0x0305F528>
for key,group in l2:
result={key:list(group)}
print (result)
'''
Output:
{'color': [('color', 'red'), ('color', 'blue'), ('color', 'green')]}
{'Numbers': [('Numbers', 1), ('Numbers', 5)]}
'''

#list doesn't contain sorted data based on the key function.It breaks the group every time,when value of key function changes.
l1=[('color','red'),('color','blue'),('color','green'),('Numbers',1),('Numbers',5),('color','purple')]
l2=itertools.groupby(l1,key=lambda x:x)
print (l2)#Output:<itertools.groupby object at 0x028AF5A0>

for key,group in l2:
result={key:list(group)}
print (result)
'''
Output:
{'color': [('color', 'red'), ('color', 'blue'), ('color', 'green')]}
{'Numbers': [('Numbers', 1), ('Numbers', 5)]}
{'color': [('color', 'purple')]}
'''

#key is not mentioned
l1=[('color','red'),('color','blue'),('color','green'),('Numbers',1),('Numbers',5),('color','purple')]
l2=itertools.groupby(l1)
print (l2)#Output:<itertools.groupby object at 0x028AF578>
for key,group in l2:
result={key:list(group)}
print (result)
'''
Output:
{('color', 'red'): [('color', 'red')]}
{('color', 'blue'): [('color', 'blue')]}
{('color', 'green'): [('color', 'green')]}
{('Numbers', 1): [('Numbers', 1)]}
{('Numbers', 5): [('Numbers', 5)]}
{('color', 'purple'): [('color', 'purple')]}

'''``````

Combinatorial generators:

1. `product()`

Cartesian product of iterable objects supplied as input.

Definition of Cartesian Product: Product of a Set X and many Y Is a set containing all ordered pairs (x, y), in which x belongs to the set X, and y belongs to the set Y

To compute the product of an iterable object multiplied by itself, you need to specify the number of repetitions using the optional keyword argument repeat… For instance, product (A, repeat = 4) – the same as product (A, A, A, A)

`itertools.product(*iterables,repeat)`

``````import itertools
#Only one iterable is given
l1=itertools.product("ABCD")
print (list(l1))#Output:[('A',), ('B',), ('C',), ('D',)]

#two iterables are given
l2=itertools.product("ABC",[1,2])
print (list(l2))#Output:[('A', 1), ('A', 2), ('B', 1), ('B', 2), ('C', 1), ('C', 2)]

#one iterable and repeat is mentioned.
l3=itertools.product("xy",repeat=2)
print (list(l3))#Output:[('x', 'x'), ('x', 'y'), ('y', 'x'), ('y', 'y')]

l4=itertools.product("aa",repeat=2)
print (list(l4))#Output:[('a', 'a'), ('a', 'a'), ('a', 'a'), ('a', 'a')]

#More than two iterables is mentioned
l5=itertools.product([1,2],[3,4],[5,6])
print (list(l5))#Output:[(1, 3, 5), (1, 3, 6), (1, 4, 5), (1, 4, 6), (2, 3, 5), (2, 3, 6), (2, 4, 5), (2, 4, 6)]``````

2`permutations()`

Returns sequential `r` permutations of elements in an iterable object. If r is not specified or is set to None, then by default r takes the length of the iterable and generates all possible full permutations. Permutation tuples are output in lexicographic order according to the iteration order of the input data. Thus, if the input data of the iterable is sorted, then the combination of tuples will be returned in sorted order.

Elements are considered unique based on their position, not their meaning. Thus, if the input elements are unique, then there will be no duplicate values ​​in each permutation.

`itertools.permutations(iterable,r=None)`

``````import itertools
l1=itertools.permutations("ABC")
print (list(l1))#Output:[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]

l2=itertools.permutations([3,2,1])
print (list(l2))#Output:[(3, 2, 1), (3, 1, 2), (2, 3, 1), (2, 1, 3), (1, 3, 2), (1, 2, 3)]

#elements are treated as unique based on their position and not by their value.
l3=itertools.permutations([1,1])
print (list(l3))#Output:[(1, 1), (1, 1)]

l4=itertools.permutations(["ABC"])
print (list(l4))#Output:[('ABC',)]

#r value is mentioned as 2. It will return all different permutations in 2 values.
l5=itertools.permutations([1,2,3,4],2)
print (list(l5))#Output:[(1, 2), (1, 3), (1, 4), (2, 1), (2, 3), (2, 4), (3, 1), (3, 2), (3, 4), (4, 1), (4, 2), (4, 3)]``````

Note: in permutations, the order of the elements matters.

3`combinations()`

Returns subsequences of length `r` from the elements of the iterable as input.

The combination of tuples is generated in lexicographic order according to the order of the elements of the iterable in the input. Thus, if the input iterable is sorted, then the combination of tuples will be generated in sorted order.

Lexicographic order is a way of ordering words alphabetically.

Elements are considered unique based on their position, not their value. This way, if the output elements are unique, then there will be no duplicate values ​​in each combination.

`itertools.combinations(iterable, r)`

4`combination_swith_replacement()`:

Returns subsequences of length `r` from the elements of the iterable object supplied as an input, while individual elements can be repeated more than once.

`itertools.combinations_with_replacement (iterable, r)`

``````import itertools
l1=itertools.combinations("ABC",2)
print (list(l1))#Output:[('A', 'B'), ('A', 'C'), ('B', 'C')]
l1=itertools.combinations_with_replacement("ABC",2)
print (list(l1))#Output:[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]

l2=itertools.combinations([3,2,1],3)
print (list(l2))#Output:[(3, 2, 1)]
l2=itertools.combinations_with_replacement([3,2,1],3)
print(list(l2))#Output:[(3, 3, 3), (3, 3, 2), (3, 3, 1), (3, 2, 2), (3, 2, 1), (3, 1, 1), (2, 2, 2), (2, 2, 1), (2, 1, 1), (1, 1, 1)]

#elements are treated as unique based on their position and not by their value.
l3=itertools.combinations([1,1],2)
print (list(l3))#Output:[(1, 1)]
l3=itertools.combinations_with_replacement([1,1],2)
print (list(l3))#Output:[(1, 1), (1, 1), (1, 1)]

#since list contains only one element, given r value is 2. So it returns empty list.
l4=itertools.combinations(["ABC"],2)
print (list(l4))#Output:[]
#In combinations_with_replacement,it allows repeated element.
l4=itertools.combinations_with_replacement(["ABC"],2)
print (list(l4))#Output:[('ABC', 'ABC')]

#r value is not mentioned. It will raise TypeError
#l5=itertools.combinations([1,2,3,4])
#print (list(l5))#Output:TypeError: combinations() missing required argument 'r' (pos 2)
l5=itertools.combinations_with_replacement([1,2,3,4])
print (list(l5))#Output:TypeError: combinations_with_replacement() missing required argument 'r' (pos 2)``````

Note:

1. Used as an argument in `map()` and `zip()`:

• `count() `

• `repeat()`

`repeat()` – passing a stream of constant values ​​to a function `map()` or `zip()`

`count() `– will feed different values ​​to the function `map()` or `zip()`

2. The difference between `cycle()` and `repeat()`:

`cycle()` iterates over the same object over and over;

`repeat()` returns the same object over and over again.

3. The difference between `reduce()` and `itertools.accumulate()`:

`reduce()`:

• Returns only the final amount;

• The first argument must be a function, and the second must be an iterable object.

`accumulate()`:

• Returns the current accumulated value. The elements in the output iterable will be equal to the elements in the input if no initial value is provided.

• The first argument must be an iterable object, and the second must be a function.

4. The difference between` filter()` and `itertools.compress()`:

• Function` filter()` filters the given iterable with a function that checks if each element is True or not.

• Function `compress()` filters the specified iterable based on the matching element in the selector parameter. An iterable object with True / False values ​​is specified as the selector.

5. The difference between` filter()` and `itertools.filterfalse()`:

• `filter()`: creates an iterator from the elements of the iterable for which the specified function returns True.

• `filterfalse()`: Creates an iterator from the elements of an iterable object for which the specified function returns False.

6. The difference between `zip()` and `itertools.zip_longest()`:

• `zip()`: The iteration continues until the shortest iterable object ends.

• `zip_longest()`: The iteration continues until the longest iterable object ends.

7. The difference between a list slice and `itertools.islice()`:

• Slicing the list creates a new list;

• `islice()` – returns an iterator. With the help of an iterator, we can organize the loop as we like.

8. The difference between` itertools.permutations()` and `itertools.combinations()`:

• `itertools.permutations()`: The order of the elements matters;

• `itertools.combinations()`: The order of the elements doesn’t matter.

Combinations and permutations do not contain duplicate values.

9. The difference between `itertools.combinations()` and `itertools.combination_swith_replacement`:

• `combinations()`: The order of the elements is irrelevant and the values ​​are not repeated.

• `combination_swith_replacement()`: The order of the items is irrelevant and the values ​​are repeated.

10. The difference between `itertools.takewhile()` and `itertools.dropwhile()`:

• `takewhile()`: Creates an iterator that returns elements from an iterable as long as the predicate is True.

• `dropwhile()`: Creates an iterator that discards elements from an iterable while the predicate is True, then returns each element.

Sources: