– `itertools.chain(iterable1, iterable2, …)`: Объединяет несколько итерируемых объектов в один длинный итератор.

– `itertools.islice(iterable, start, stop, step)`: Возвращает срез итерируемого объекта, начиная с `start` и заканчивая до `stop` с шагом `step`.

– `itertools.filterfalse(predicate, iterable)`: Возвращает элементы итерируемого объекта, для которых функция `predicate` возвращает `False`.

– `itertools.groupby(iterable, key)`: Группирует элементы из итерируемого объекта на основе функции `key`.

– `itertools.product(iterable1, iterable2, …)`: Возвращает декартово произведение нескольких итерируемых объектов.

Давайте рассмотрим пример применения модуля `itertools` для оптимизации и измерения производительности кода. Предположим, у нас есть два больших списка, и мы хотим найти пересечение (общие элементы) между ними. Мы можем использовать модуль `itertools` для этой задачи:

```python

import timeit

import itertools

# Создадим два больших списка

list1 = list(range(100000))

list2 = list(range(50000, 150000))

# Измерим время выполнения операции поиска пересечения с использованием цикла

def find_intersection_with_loop():

intersection = []

for item in list1:

if item in list2:

intersection.append(item)

# Измерим время выполнения операции поиска пересечения с использованием itertools

def find_intersection_with_itertools():

intersection = list(itertools.filterfalse(lambda x: x not in list2, list1))

# Измерим время выполнения для поиска с использованием цикла

loop_time = timeit.timeit(find_intersection_with_loop, number=100)

print(f"Поиск с использованием цикла занял {loop_time:.6f} секунд")

# Измерим время выполнения для поиска с использованием itertools

itertools_time = timeit.timeit(find_intersection_with_itertools, number=100)

print(f"Поиск с использованием itertools занял {itertools_time:.6f} секунд")

```

Этот код измеряет время выполнения операции поиска пересечения между двумя списками с использованием цикла и с использованием `itertools`. Здесь мы используем функцию `itertools.filterfalse`, чтобы найти элементы, которые присутствуют в `list1`, но отсутствуют в `list2`. Мы выполняем каждую операцию поиска 100 раз и выводим результаты.

Вы увидите, что операция поиска с использованием `itertools` обычно выполняется быстрее, чем операция с использованием цикла, что позволяет улучшить производительность кода при работе с большими данными.


4. Модуль `functools`

Модуль `functools` в Python предоставляет полезные функции для оптимизации работы с функциями. Одной из наиболее важных функций этого модуля является `lru_cache`, которая позволяет кешировать результаты функций. Это может существенно повысить производительность функций, вызываемых многократно с одними и теми же аргументами.

Разберем пример использования `lru_cache` для оптимизации функции, вычисляющей факториал числа:

```python

import functools

# Декорируем функцию с lru_cache для кеширования результатов

@functools.lru_cache(maxsize=None)

def factorial(n):

if n == 0:

return 1

else:

return n factorial(n – 1)

# Теперь функция будет кешировать результаты

result1 = factorial(5) # Первый вызов, вычисляется и кешируется

result2 = factorial(5) # Второй вызов, результат взят из кеша, не вычисляется снова

print(result1) # Вывод: 120

print(result2) # Вывод: 120

```

В этом примере мы использовали `@functools.lru_cache(maxsize=None)` для декорирования функции `factorial`. Это означает, что при использовании результаты функции будут кешироваться бесконечно или, точнее, пока доступной памяти достаточно для хранения кеша. Когда функция вызывается с определенными аргументами, результат вычисления сохраняется в кеше. При последующих вызовах этой функции с теми же аргументами результат будет взят из кеша, а не будет вычисляться заново.