29.28M

Модуль 2 презентация 3

1.

Расширенные коллекции
Python
Модули collections и itertools: мощные инструменты для эффективной
работы с данными

2.

О чем эта лекция
Модуль collections
Модуль itertools
Практика
Специализированные коллекции
Инструменты для работы с
Реальные примеры применения и
для решения типичных задач:
итераторами: бесконечные
оптимизация производительности
Counter, defaultdict, deque,
последовательности,
namedtuple
комбинаторика, функциональная
обработка
Модуль 2: Коллекции и структуры данных | Тема 3 | 2 академических часа

3.

Цели занятия
01
02
Специализированные коллекции
Подсчет и группировка
Научиться использовать Counter, defaultdict, deque и
Применять Counter для анализа частоты элементов и
namedtuple для решения практических задач
defaultdict для эффективной группировки данных
03
04
Операции с последовательностями
Структурированные данные
Работать с deque для быстрых операций в начале и конце
Создавать читаемые структуры данных с помощью
коллекций
namedtuple
05
06
Инструменты itertools
Оптимизация алгоритмов
Использовать функции модуля itertools для эффективной
Создавать эффективные решения для обработки больших
работы с итераторами
объемов данных

4.

Модуль collections
Специализированные коллекции Python

5.

Counter: подсчет элементов
Что такое Counter?
from collections import Counter
Counter — это специализированный словарь для подсчета
количества элементов в коллекции. Это один из самых
# Подсчет символов
полезных инструментов для анализа данных, статистики и
text_ counter = Counter("hello world")
работы с текстом.
print(text_ counter)
Основные преимущества:
Автоматический подсчет элементов
Методы для получения самых частых элементов
Арифметические операции над счетчиками
Простота использования
# Counter({'l': 3, 'o': 2, 'h': 1, ...})
# Подсчет элементов списка
fruits = ['apple', 'banana', 'apple’,
'orange', 'banana', 'apple’]
fruit_ counter = Counter(fruits)
print(fruit_ counter)
# Counter({'apple': 3, 'banana': 2, ...})

6.

Создание Counter
1
Из строки
2
Из списка
counter = Counter("python")
counter = Counter([1, 2, 1, 3, 2, 1])
# Counter({'p': 1, 'y': 1, ...})
# Counter({1: 3, 2: 2, 3: 1})
3
Из словаря
4
Пустой Counter
counter = Counter({'A': 5, 'B': 3})
counter = Counter()
# Counter({'A': 5, 'B': 3})
# Counter()
Counter принимает любой итерируемый объект и автоматически подсчитывает количество вхождений каждого элемента. Это
намного удобнее, чем писать циклы и условия вручную.

7.

Основные методы Counter
most_common(n)
Возвращает список из n самых частых элементов с их количеством
counter.most_common(3)
elements()
Возвращает итератор по всем элементам,
повторяя каждый столько раз, сколько он встречается
list(counter.elements())
update()
Добавляет элементы к существующему счетчику
counter.update(['a', 'b'])
subtract()
Вычитает элементы из счетчика
(может давать отрицательные значения)
counter.subtract(['a', 'b'])

8.

Арифметические операции с Counter
Операции над счетчиками
Counter поддерживает математические операции,
counter1 = Counter(['a', 'b', 'c', 'a’])
counter2 = Counter(['a', 'b', 'b', 'd’])
что делает его невероятно удобным для анализа
данных:
Сложение (+) — объединяет счетчики
Вычитание (-) — находит разницу (оставляет
# Сложение
print(counter1 + counter2)
# Counter({'a': 3, 'b': 3, 'c': 1 , 'd': 1 })
только положительные)
# Вычитание
Пересечение (&) — минимальное значение для
print(counter1 - counter2)
каждого ключа
# Counter({'a': 1 , 'c': 1 })
Объединение (|) — максимальное значение для
каждого ключа
# Пересечение
print(counter1 & counter2)
# Counter({'a': 1 , 'b': 1 })
# Объединение
print(counter1 | counter2)
# Counter({'a': 2, 'b': 2, 'c': 1 , 'd': 1 })

9.

Практический пример: анализ текста
from collections import Counter
# Анализ частоты слов в тексте
text = """Python is a powerful programming language.
Python is easy to learn and Python is widely used.""“
# Разбиваем текст на слова и приводим к нижнему регистру
words = text.lower().replace('.', '').replace(',', '').split()
# Подсчитываем частоту слов
word_counter = Counter(words)
print("Самые частые слова:")
for word, count in word_counter.most_common(5):
print(f" '{word}': {count} раз")
# Результат:
# 'python': 3 раза
# 'is': 3 раза# 'a': 1 раз
# 'powerful': 1 раз
# 'programming': 1 раз
Counter идеально подходит для анализа текстов, логов, данных о продажах и любых других коллекций, где нужно подсчитать частоту элементов.

10.

defaultdict: словари со значениями по
умолчанию
Проблема обычных словарей
Решение с defaultdict
При работе с обычными словарями
defaultdict автоматически создает
часто приходится проверять наличие
значение по умолчанию для
ключа перед его использованием:
несуществующих ключей:
regular_dict = {}
from collections import defaultdict
words = ["apple", "banana", "apple"]
length_groups = defaultdict(list)
for word in words:
length = len(word)
for word in words:
if length not in regular_ dict:
length_groups[len(word)].append(word)
regular_ dict[length] = []
regular_dict[length].append(word)
# {5: ['apple', 'apple’],
# 6: ['banana']}
Это многословно и неудобно, особенно
при группировке данных.
Код стал короче, проще и понятнее!

11.

Фабрики значений defaultdict
defaultdict(list)
defaultdict(int)
Для группировки элементов в списки
Для счетчиков (начальное значение 0)
groups = defaultdict(list)
counter = defaultdict(int)
groups['key'].append(value)
counter['key'] += 1
defaultdict(set)
defaultdict(lambda)
Для коллекций уникальных элементов
Для пользовательских значений
unique = defaultdict(set)
custom = defaultdict(lambda: [])
unique['key'].add(value)
nested = defaultdict(lambda: defaultdict(int))
Первый аргумент defaultdict — это фабрика, функция без параметров, которая возвращает значение по умолчанию для новых
ключей.

12.

Практический пример: группировка студентов
from collections import defaultdict
# Данные о студентах и их оценках
students_grades = [
("Анна", "Математика", 5),
("Боб", "Физика", 4),
("Анна", "Физика", 5),
("Боб", "Математика", 3),
("Катя", "Математика", 5),
("Катя", "Физика", 4)
]
# Выводим результат
print("Оценки по студентам:")
for student, subjects in grades_by_student.items():
print(f" {student}: {dict(subjects)}")
# Результат:
# Анна: {'Математика': 5, 'Физика': 5}
# Боб: {'Физика': 4, 'Математика': 3}
# Катя: {'Математика': 5, 'Физика': 4}
# Группируем оценки по студентам
grades_by_student = defaultdict(lambda:
defaultdict(int))
for student, subject, grade in students_grades:
grades_by_student[student][subject] = grade
defaultdict особенно полезен при построении сложных вложенных структур данных без необходимости вручную инициализировать каждый уровень.

13.

Словарь с управляемым порядком OrderedDict
OrderedDict – словарь, который сохраняет порядок добавления элементов и позволяет им гибко
управлять.
Когда он нужен, если обычный dict тоже хранит
порядок?
Нужны специальные операции, например
move_to_end()
Нужно явно показывать, что порядок важен
Основные особенности:
Порядок элементов – часть структуры
Метод move_to_end(key, last = True)
Метод popitem(last = True) удаляет первый либо
последний элемент
from collections import
OrderedDict
d = OrderedDict()
d['A'] = 1
d['B'] = 2
d.move_ to_ end('A')
print(d) # B, A

14.

Объединение словарей ChainMap
ChainMap – структура, которая объединяет несколько словарей в один «виртуальный»
Данная структура ищет элемент по такому принципу:
Сначала в первом словаре
Потом во втором
Потом в третьем и т. д.
Зачем нужно?
Объединение конфигураций (default + user settings)
Создание слоя «переопределений»
Упрощение работы с переменными окружения
from collections import
ChainMap
defaults = {'theme': 'light',
'lang': 'ru'}
user = {'theme': 'dark'}
settings = ChainMap(user,
defaults)
print(settings['theme']) # 'dark'
print(settings['lang']) # 'ru'

15.

deque: двусторонняя очередь
Что такое deque?
from collections import deque
deque (double-ended queue) — это двусторонняя очередь,
оптимизированная для быстрых операций добавления и
# Создание dequed = deque([1, 2, 3, 4, 5])
удаления элементов с обоих концов.
Преимущества перед списком:
O(1) для добавления/удаления с обоих концов
Операции append/pop с любой стороны очень
быстрые
Поддержка ротации элементов
Ограничение максимального размера
Список Python требует O(n) для операций в начале, что
медленно для больших данных.
# Добавление в конец
d.append(6) # [1, 2, 3, 4, 5, 6]
# Добавление в начало
d.appendleft(0) # [0, 1, 2, 3, 4, 5, 6]
# Удаление из конца
d.pop() # Вернет 6
# Удаление из начала
d.popleft() # Вернет 0
print(d) # deque([1, 2, 3, 4, 5])

16.

Основные операции deque
append() / appendleft()
pop() / popleft()
rotate(n)
extend() / extendleft()
Добавление элемента справа
Удаление и возврат элемента
Ротация элементов вправо (n
Добавление нескольких
или слева за O(1)
справа или слева за O(1)
> 0) или влево (n < 0)
элементов справа или слева

17.

Ротация deque
from collections import deque
Как работает ротация
Ротация перемещает элементы с одного конца deque на
d = deque([1, 2, 3, 4, 5])
print(f"Исходная: {d}")
другой:
их в начало
# Ротация вправо на 2 позиции
d.rotate(2)
print(f"Вправо на 2: {d}")
# deque([4, 5, 1, 2, 3])
# Ротация влево на 3 позиции
d.rotate(-3)
print(f"Влево на 3: {d}")
# deque([2, 3, 4, 5, 1])
rotate(n > 0) — берет n элементов справа и перемещает
rotate(n < 0) — берет |n| элементов слева и перемещает
их в конец
Это очень эффективная операция, выполняемая за O(k), где
k — количество перемещаемых элементов.

18.

Ограниченная deque
from collections import deque
# Создаем deque с максимальным размером 3
limited_deque = deque(maxlen= 3)
print("Добавляем элементы от 0 до 5:")
for i in range(6):
limited_deque.append(i)
print(f" Добавили {i}: {limited_deque}")
# Результат:
# Добавили 0: deque([0], maxlen= 3)
# Добавили 1: deque([0, 1], maxlen= 3)
# Добавили 2: deque([0, 1, 2], maxlen= 3)
# Добавили 3: deque([1, 2, 3], maxlen= 3) ← старый элемент удалился
# Добавили 4: deque([2, 3, 4], maxlen= 3)
# Добавили 5: deque([3, 4, 5], maxlen= 3)
Когда deque достигает максимального размера, добавление нового элемента автоматически удаляет самый старый элемент с
противоположного конца. Это идеально для реализации кольцевых буферов, истории действий с ограниченным размером, скользящих
окон и кэшей.

19.

Практический пример: история браузера
class BrowserHistory:
def __init__(self, max_history=1 0):
self.history = deque(maxlen=max_history)
self.current_pos = -1
def visit(self, url):
# Удаляем "будущую" историю при новом переходе
while len(self.history) > self.current_pos + 1 :
self.history.pop()
self.history.append(url)
self.current_pos = len(self.history) – 1
print(f"Посетили: {url}")
def back(self):
if self.current_pos > 0:
self.current_pos -= 1
return self.history[self.current_pos]
return None
def forward(self):
if self.current_pos < len(self.history) - 1 :
self.current_pos += 1
return self.history[self.current_pos]
return None
# Использование
browser = BrowserHistory(max_history=5)
browser.visit("google.com")
browser.visit("github.com")
browser.visit("python.org")
print(f"Назад: {browser.back()}") # github.com
print(f"Вперед: {browser.forward()}") # python.org

20.

deque vs list – коротко и ясно
Операция
list
deque
append( )
O(1)
O(1)
appendleft( )
O(n)
O(1)
pop()
O(1)
O(1)
popleft( )
O(n)
O(1)
Какие выводы?
deque – лучшая структура для очередей, буферов, истории действий.

21.

namedtuple
Именованные кортежи для структурированных данных

22.

Что такое namedtuple?
Обычный кортеж
# Координаты точки
namedtuple
from collections import namedtuple
point = (10, 20)
# Создаем класс Point
# Доступ по индексу – неясно
Point = namedtuple('Point', ['x', 'y’])
print(point[0]) # x или y?
point = Point(10, 20)
print(point[1]) # что это?
# Доступ по имени - понятно!
# Данные о человеке
print(point.x) # 10
person = ("Анна", 25, "Москва")
print(point.y) # 20
print(person[0]) # имя?
print(person[1]) # возраст? рост?
# Данные о человеке
Person = namedtuple('Person', 'name age city’)
Проблема: код трудно читать, легко перепутать порядок
person = Person("Анна", 25, "Москва")
значений
print(person.name) # Анна
print(person.age)
# 25
Решение: читаемый, самодокументируемый код

23.

Создание namedtuple
Способ 1: Список полей
Способ 2: Строка с пробелами
Способ 3: Строка с запятыми
Point = namedtuple('Point’,
Person = namedtuple('Person’,
Car = namedtuple('Car', 'make,
['x', 'y'])
'name age city')
model, year')
Первый аргумент — имя создаваемого класса, второй — список или строка с названиями полей. После создания можно
использовать как обычный класс для создания экземпляров.

24.

Преимущества namedtuple
Неизменяемость
Эффективность памяти
Как и обычные кортежи, namedtuple неизменяемы — нельзя
Занимают меньше памяти, чем словари и обычные классы
изменить значения после создания
Читаемость
Производительность
Код становится самодокументируемым благодаря именованным
Быстрее словарей для доступа к данным
полям
Доступ по индексу
Совместимость
Поддерживают доступ как по имени, так и по индексу
Работают везде, где работают обычные кортежи

25.

Методы namedtuple
Специальные методы
_replace() — создание копии с
P erson = namedtuple('P erson', 'name age city’)
person = P erson("Анна", 25, "Москва")
изменениями
_asdict() — преобразование в словарь
_fields — кортеж с именами полей
_make() — создание из итерируемого
Эти методы начинаются с подчеркивания,
чтобы не конфликтовать с
пользовательскими именами полей.
# Создание копии с изменениями
older = person._ replace(age=26)
print(older) # P erson(name='Анна', age=26, city='Москва’)
# Преобразование в словарь
person_ dict = person._ asdict()
print(person_ dict) # {'name': 'Анна', 'age': 25, ...}
# Список полей
print(P erson._ fields) # ('name', 'age', 'city’)
# Создание из списка
data = ['Боб', 30, 'Санкт-Петербург’]
person2 = P erson._ make(data)
print(person2) # P erson(name='Боб', age=30, ...)

26.

Практический пример: база данных студентов
from collections import namedtuple, defaultdict
Объяснение
В этом примере мы используем namedtuple для создания структуры Student с полями id, name,
# Определяем структуру студента
group и grades. Далее создается список объектов Student.
Student = namedtuple('Student', 'id name group grades’)
Код демонстрирует два основных действия:
# Создаем базу данных студентов
students = [
Student(1, "Анна Иванова", "ПИ-20", [5, 4, 5, 4]),
Student(2, "Боб Петров", "ПИ-20", [4, 5, 3, 4]),
Student(3, "Катя Сидорова", "ИС-20", [5, 5, 4, 5]),
Student(4, "Дима Козлов", "ИС-20", [4, 4, 4, 5])
]
# Выводим инф ормацию с расчетом среднего балла
print("База данных студентов:")
for student in students:
avg_ grade = sum(student.grades) / len(student.grades)
print(f" {student.name} ({student.group}): средний балл {avg_grade:.1f}")
# Группируем по группам
groups = defaultdict(list)
for student in students:
groups[student.group].append(student)
print("\nГруппировка по группам:")
for group, group_students in groups.items():
names = [s.name for s in group_students]
print(f" {group}: {names}")
1.
Вывод информации о студентах: Для каждого студента рассчитывается и выводится
средний балл.
2.
Группировка по группам: Используется defaultdict для группировки студентов по их
учебным группам, после чего выводится список имен студентов для каждой группы.
Результат выполнения
База данных студентов:
Анна Иванова (ПИ-20): средний балл 4.5
Боб Петров (ПИ-20): средний балл 4.0
Катя Сидорова (ИС-20): средний балл 4.8
Дима Козлов (ИС-20): средний балл 4.3
Группировка по группам:
ПИ-20: ['Анна Иванова', 'Боб Петров’]
ИС-20: ['Катя Сидорова', 'Дима Козлов']

27.

Модуль itertools
Мощные инструменты для работы с итераторами

28.

Зачем нужен itertools?
Проблемы без itertools
Решение с itertools
Генерация больших последовательностей занимает
Ленивые вычисления экономят память
много памяти
Встроенные функции для комбинаторики
Комбинаторные задачи требуют сложных циклов
Композиция простых операций
Обработка данных часто неэффективна
Читаемый и элегантный код
Код для типичных паттернов повторяется
# Эффективно: создаем итераторs
# Неэффективно: создаем весь список
quares = (x**2 for x in range(1000000))
squares = [x**2 for x in range(1000000)]
# Занимает ~100 байт!
# Занимает ~4 МБ памяти!
# Простой и понятный код
# Сложный код для комбинаций
from itertools import combinations
combinations = []
combos = combinations(items, 2)
for i in range(len(items)):
# Одна строка вместо вложенных циклов!
for j in range(i+ 1, len(items)):
combinations.append((items[i], items[j]))

29.

Что такое Итератор?
Итератор – объект, который выдает совокупность элементов по одному.
Он не хранит всю последовательность – элементы
вычисляются по необходимости.
При помощи итератора вы можете пройтись только
один раз
Итератор создается при помощи функции iter( ), а
используется функцией next( ).
# Пример использования итератора
nums = iter([10, 20, 30])
next(nums) # 10
next(nums) # 20
next(nums) # 30
Зачем мы его проходим?
Мы проходим итератор для более глубокого понимания, зачем пользователю функции itertools и
как они экономят память, а также почему мы не можем пройтись назад или дважды итератором.

30.

Бесконечные итераторы
count(start, step)
cycle(iterable)
repeat(elem, n)
Бесконечная арифметическая
Циклическое повторение
Повторение одного элемента
прогрессия
элементов
count(1 0, 2) # 1 0, 12, 14, 16...
cycle(['A', 'B', 'C']) # A, B, C, A, B, C…
repeat('X', 5) # X, X, X, X, X
Важно: Бесконечные итераторы никогда не заканчиваются! Используйте их с функциями, которые ограничивают
количество элементов (islice, zip, takewhile), или в циклах с условием выхода.

31.

Примеры бесконечных итераторов
import itertools
# 1. count() - нумерация строк файла
lines = ["Первая строка", "Вторая строка", "Третья строка"]
for num, line in zip(itertools.count(1), lines):
print(f"{num}. {line}")
# 2. cycle() - A/B тестирование
ab_pattern = itertools.cycle(['A', 'B’])
test_assignments = list(itertools.islice(ab_pattern, 1 0))
print(f"A/B группы: {test_assignments}")
# ['A', 'B', 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B’]
# 3. repeat() - заполнение значениями по умолчанию
data = [1, 2, 3]
padded = list(itertools.islice(
itertools.chain(data, itertools.repeat(0)), 8
))
print(f"Дополненные данные: {padded}")
# [1, 2, 3, 0, 0, 0, 0, 0]
# 4. Комбинация count() и zip() для словаря
items = ['apple', 'banana', 'cherry’]
indexed = dict(zip(items, itertools.count()))
print(indexed) # {'apple': 0, 'banana': 1 , 'cherry': 2}

32.

Жизненные примеры
1. Counter – анализ сообщений в чате. Например, сколько раз
было написано слово «привет» или «пока».
2. deque – история действий в игре, то есть последние N
ходов игрока.
3. product – все возможные образы человека, которые состоят
из футболки/шорт/штанов.
4. permutations – порядок выступлений. То есть все возможные
очереди участников.
5. chain – объединение файлов домашек. То есть кладем все
ответы по предметам в одну последовательность.

33.

Комбинаторные итераторы
product(*iterables, repeat=1)
permutations(iterable, r)
Декартово произведение — все комбинации элементов из
Размещения — упорядоченные выборки без повторений
нескольких последовательностей
(порядок важен)
product(['A', 'B'], [1, 2]) # ('A',1), ('A',2), ('B',1), ('B',2)
permutations([1, 2, 3], 2) # (1,2), (1,3), (2,1), (2,3), (3,1), (3,2)
combinations(iterable, r)
combinations_with_replacement(iterable, r)
Сочетания — неупорядоченные выборки без повторений
Сочетания с повторениями — можно выбирать один элемент
несколько раз
combinations([1, 2, 3], 2) # (1,2), (1,3), (2,3)
combinations_with_replacement([1, 2], 2) # (1,1), (1,2), (2,2)

34.

Функциональные итераторы: chain и compress
chain() — объединение
compress() — фильтрация по маске
Объединяет несколько итераторов в один непрерывный
Выбирает элементы согласно булевой маске:
поток:
list1 = [1, 2, 3]
data = ['A', 'B', 'C', 'D', 'E’]
list2 = [4, 5, 6]
mask = [1, 0, 1, 0, 1] # True/ False или 1/ 0
list3 = [7, 8, 9]
filtered = list(itertools.compress(data, mask))
combined = list(itertools.chain(list1, list2, list3))
print(filtered) # ['A', 'C', 'E’]
print(combined) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Пример: выбор победителей
# Для вложенных списков
participants = ['Анна', 'Боб', 'Катя', 'Дима’]
nested = [[1, 2], [3, 4], [5, 6]]
winners = [True, False, True, False]
flat = list(itertools.chain.from_iterable(nested))
result = list(itertools.compress(participants, winners))
print(flat) # [1, 2, 3, 4, 5, 6]
print(result) # ['Анна', 'Катя']

35.

dropwhile, takewhile и filterfalse
takewhile(predicate, iterable)
dropwhile(predicate, iterable)
filterfalse(predicate, iterable)
Берет элементы пока условие
Пропускает элементы пока условие
Обратный filter — возвращает
истинно, затем останавливается
истинно, затем возвращает все
элементы, для которых условие ложно
остальные
numbers = [1 , 3, 5, 8, 9, 1 2, 1 5]
numbers = [1 , 3, 5, 8, 9, 1 2, 1 5]
numbers = range(1 , 11 )
odd = list(itertools.takewhile(
after = list(itertools.dropwhile(
odd = list(itertools.filterfalse(
lambda x: x % 2 == 1 , numbers
lambda x: x % 2 == 1 , numbers
lambda x: x % 2 == 0, numbers
))
))
))
# [1 , 3, 5] - остановились на 8
# [8, 9, 1 2, 1 5] - начали с 8
# [1 , 3, 5, 7, 9] - нечетные

36.

groupby: группировка элементов
import itertools
students = [
('Анна', 'ПИ-20'), ('Боб', 'ПИ-20’),
('Катя', 'ИС-20'), ('Дима', 'ИС-20’),
('Лена', 'ПИ-21 ’)
]
# ВАЖНО: groupby требует предварительной сортировки!
students_ sorted = sorted(students, key=lambda x: x[1 ])
print("Студенты по группам:")
for group, group_ students in itertools.groupby(students_ sorted, key=lambda x: x[1 ]):
names = [student[0] for student in group_ students]
print(f" {group}: {names}")
# Результат:
# ИС-20: ['Катя', 'Дима’]
# ПИ-20: ['Анна', 'Боб’]
# ПИ-21 : ['Лена']
Важная особенность: groupby группирует только последовательные элементы с одинаковым ключом! Поэтому данные нужно предварительно
отсортировать по ключу группировки. Если сортировка невозможна или нежелательна, используйте defaultdict.

37.

groupby: Ошибки при использовании
Он группирует только соседние элементы с одинаковым ключом. Поэтому данные необходимо
отсортировать тем же ключом, что и groupby
Неверное использование (без сортировки):
groups = itertools.groupby(students, key=lambda x: x.group)
Верное:
students_sorted = sorted(students, key=lambda x: x.group)
groups = itertools.groupby(students_sorted, key=lambda x: x.group)
Совет: если заметили странное поведение groupby – проверьте, отсортированны ли данные перед
его применением

38.

islice: срезы для итераторов
Зачем нужен islice?
import itertools
Обычные срезы [start:stop:step] не работают с итераторами,
потому что итераторы нельзя индексировать. islice решает
# Создаем бесконечную последовательность
эту проблему:
numbers = itertools.count(0, 2) # 0, 2, 4, 6...
Работает с любыми итераторами
# Берем элементы с 5 по 1 0
Не загружает данные в память
slice_ 5_ to_ 1 0 = list(itertools.islice(
Поддерживает start, stop и step
numbers, 5, 1 1
Синтаксис:
islice(it, stop)
islice(it, start, stop)
islice(it, start, stop, step)
))
print(slice_ 5_ to_ 1 0) # [1 0, 1 2, 1 4, 1 6, 1 8, 20]
# Каждый третий элемент из первых 20
numbers = itertools.count(1 )
every_ third = list(itertools.islice(
numbers, 0, 20, 3
))
print(every_ third) # [1 , 4, 7, 1 0, 1 3, 1 6, 1 9]
# Первые 5 элементов любого итератора
data = (x**2 for x in range(1 000000))
first_ five = list(itertools.islice(data, 5))
print(first_ five) # [0, 1 , 4, 9, 1 6]

39.

accumulate: накопительные вычисления
import itertools
import operator
numbers = [1 , 2, 3, 4, 5]
# 1 . Накопительная сумма (по умолчанию)
cumsum = list(itertools.accumulate(numbers))
print(f"Числа: {numbers}")
print(f"Накопительная сумма: {cumsum}")# [1, 3, 6, 10, 1 5]
# 2. Накопительное произведение
cumprod = list(itertools.accumulate(numbers, operator.mul))
print(f"Накопительное произведение: {cumprod}")
# [1 , 2, 6, 24, 1 20]
# 3. Накопительный максимум
data = [3, 1 , 4, 1 , 5, 9, 2, 6]
running_max = list(itertools.accumulate(data, max))
print(f"Данные: {data}")
print(f"Накопительный максимум: {running_max}")
# [3, 3, 4, 4, 5, 9, 9, 9]
# 4. Пользовательская ф ункция
def running_product(acc, new):
return acc * new
factorial = list(itertools.accumulate(range(1, 6), running_product))
print(f"Факториалы 1 -5: {factorial}")
# [1 , 2, 6, 24, 1 20]

40.

zip_longest и tee
zip_longest — zip с заполнением
tee — разделение итератора
Обычный zip обрезает результат по самой короткой
Создает несколько независимых копий итератора для
последовательности. zip_longest продолжает до конца самой
многократного использования:
длинной, заполняя пропуски:
data = range(1, 6)
short = [1, 2, 3]
long = ['a', 'b', 'c', 'd', 'e’]
# Создаем 3 независимых итератора
iter1, iter2, iter3 = itertools.tee(data, 3)
# Обычный zip - теряем 'd' и 'e’
normal = list(zip(short, long))
# Используем каждый для своей цели
print(normal) # [(1,'a'), (2,'b'), (3,'c’)]
squares = [x**2 for x in iter1]
cubes = [x**3 for x in iter2]
# zip_longest - сохраняем все
doubled = [x*2 for x in iter3]
extended = list(itertools.zip_longest(
short, long, fillvalue=0
print(f"Квадраты: {squares}")
))
# [1, 4, 9, 16, 25]
print(extended)
print(f"Кубы: {cubes}")
# [(1,'a'), (2,'b'), (3,'c'), (0,'d'), (0,'e')]
# [1, 8, 27, 64, 125]
print(f"Удвоенные: {doubled}")
# [2, 4, 6, 8, 10]

41.

Практика: анализ лог-файлов
from collections import Counter, defaultdict
import itertools
Объяснение функции `analyze_logs()`
Эта функция демонстрирует использование модулей collections (в частности, Counter) и базовых операций со списками
для анализа симулированных лог-записей.
def analyze_logs():
"""Анализ лог-записей с помощью collections и itertools""“
# Симулируем лог-файл
1.
(уровень логгирования, например, INFO, ERROR, WARNING).
2.
"2024-01-15 10:30:20 ERROR Database connection failed",
"2024-01-15 10:30:25 INFO User logout: user123",
Подсчет уровней: collections.Counter используется для быстрого подсчета вхождений каждого уровня
логгирования. Результат выводится в порядке убывания частоты.
log_entries = [
"2024-01-15 10:30:15 INFO User login: user123",
Извлечение уровней логгирования: С помощью генератора списка из каждой записи извлекается третий элемент
3.
Группировка ошибок: Фильтрует лог-записи, содержащие слово "ERROR", а затем извлекает время (часы и минуты)
и само сообщение об ошибке для каждой из них.
Результат выполнения:
"2024-01-15 10:30:30 WARNING High memory usage",
"2024-01-15 10:30:35 ERROR Disk space low",
Статистика по уровням:
"2024-01-15 10:30:40 INFO System backup started",
ERROR: 3
"2024-01-15 10:30:45 ERROR Network timeout",
INFO: 3
"2024-01-15 10:30:50 INFO System backup completed“
WARNING: 1
]
Ошибки по времени:
# Извлекаем уровни логирования
10:30:20: Database connection failed
log_levels = [entry.split()[2] for entry in
10:30:35: Disk space low
log_entries]
10:30:45: Network timeout
# Подсчитываем с помощью Counter level_counts = Counter(log_levels)
print("Статистика по уровням:") for level, count in level_counts.most_common(): print(f" {level}: {count}")
# Группируем ошибки по времени
error_entries = [e for e in log_entries if 'ERROR' in e]
print("\ nОшибки по времени:")
for entry in error_entries:
time = entry.split()[1][:5] # Часы:минуты
message = entry.split(' ', 3)[3]
print(f" {time}: {message}")
analyze_logs()

42.

Практика: обработка продаж
from collections import Counter, defaultdict
import itertools
# Данные: (день, товар, количество, цена)
sales_data = [
(1, 'Laptop', 2, 50000), (1, 'Mouse', 5, 1500),
(2, 'Laptop', 1, 50000), (2, 'Keyboard', 3, 3000),
(3, 'Mouse', 2, 1500), (3, 'Monitor', 1, 25000),
(1, 'Monitor', 2, 25000), (2, 'Mouse', 3, 1500)
]
Результат выполнения:
Продажи по дням:
День 1: выручка 157,500 руб.
День 2: выручка 63,500 руб.
День 3: выручка 28,000 руб.
Популярность товаров:
Mouse: 10 шт.
Laptop: 3 шт.
Keyboard: 3 шт.
# Группируем по дням
sales_by_day = defaultdict(list)
for day, product, qty, price in sales_data:
sales_by_day[day].append((product, qty, price))
print("Продажи по дням:")
for day in sorted(sales_by_day.keys()):
revenue = sum(qty * price for _, qty, price in sales_by_day[day])
print(f" День {day}: выручка {revenue:,} руб.")
# Анализ товаров с помощью Counter
products = [product for _, product, qty, _ in
sales_data for _ in range(qty)]
product_counter = Counter(products)
print("\nПопулярность товаров:")
for product, count in product_counter.most_common():
print(f" {product}: {count} шт.")
# Группировка по товарам с помощью defaultdict
revenue_by_product = defaultdict(int)
for _, product, qty, price in sales_data:
revenue_by_product[product] += qty * price
print("\nВыручка по товарам:")
for product, revenue in sorted(revenue_by_product.items(), key=lambda x: x[1], reverse=True):
print(f" {product}: {revenue:,} руб.")
Monitor: 3 шт.
Выручка по товарам:
Laptop: 150,000 руб.
Monitor: 75,000 руб.
Mouse: 15,000 руб.
Keyboard: 9,000 руб.

43.

Практика: обработка продаж
from collections import Counter, defaultdict
import itertools
# Данные: (день, товар, количество, цена)
sales_data = [ (1, 'Laptop', 2, 50000),
(1, 'Mouse', 5, 1500), (2, 'Laptop', 1, 50000),
(2, 'Keyboard', 3, 3000), (3, 'Mouse', 2, 1500),
(3, 'Monitor', 1, 25000), (1, 'Monitor', 2, 25000),
(2, 'Mouse', 3, 1500)
]
# Группируем по дням
sales_by_day = defaultdict(list)
for day, product, qty, price in sales_data:
sales_by_day[day].append((product, qty, price))
print("Продажи по дням:")
for day in sorted(sales_by_day.keys()):
revenue = sum(qty * price for _, qty, price in sales_by_day[day])
print(f" День {day}: {revenue:,} руб.")
# Топ товаров
product_quantities = Counter()
for day, product, qty, price in sales_data:
product_quantities[product] += qty
print("\ nТоп товаров:")
for product, total in product_quantities.most_common():
print(f" {product}: {total} шт.")
# Накопительная выручка
daily_revenues = []
for day in sorted(sales_by_day.keys()):
day_revenue = sum(qty * price for _, qty, price in sales_by_day[day])
daily_revenues.append(day_revenue)
cumulative = list(itertools.accumulate(daily_revenues))
print("\ nНакопительная выручка:")
for day, cum_rev in enumerate(cumulative, 1):
print(f" День {day}: {cum_rev:,} руб.")

44.

Сравнение производительности
Тест на 100,000 операций показывает, что специализированные структуры данных значительно быстрее универсальных решений.
На горизонтальной шкале указано время выполнения в миллисекундах (мс). deque особенно эффективен для операций добавления, Counter
оптимизирован для подсчета, а defaultdict устраняет накладные расходы на проверку ключей.

45.

Лучшие практики и рекомендации
Выбор правильного инструмента
Экономия памяти
Counter для подсчета, defaultdict для группировки, deque для
Используйте итераторы вместо списков для больших данных.
очередей, namedtuple для структур, itertools для ленивых
Генераторные выражения и функции itertools не загружают все в
вычислений
память
Читаемость кода
Комбинирование инструментов
Специализированные коллекции делают код
Сочетайте разные модули для решения сложных задач. Например,
самодокументируемым. namedtuple лучше кортежей, Counter яснее
Counter + defaultdict для многоуровневой статистики
циклов подсчета
Подготовка данных
Профилирование
groupby требует сортировки данных. Если порядок важен,
Измеряйте производительность для критических участков. Иногда
используйте OrderedDict или сохраняйте индексы
простое решение быстрее "оптимального"

46.

Заключение и основные выводы 1
Что мы изучили
Модуль collections:
Модуль itertools :
Counter — подсчет и статистика элементов
product — все комбинации
defaultdict — группировка без проверок ключей
combinations — выбор без порядка
deque — эффективные операции с обоих концов
permutations — порядок важен
namedtuple — структурированные данные
cycle, count, repeat — бесконечные потоки
orderedDict — управление порядком элементов
groupby — группировка соседних элементов
ChainMap — объединние словарей
accumulate — накопления

47.

Заключение и основные выводы 2
Применение в реальных задачах
Анализ данных — Counter для частотного анализа
Обработка логов — groupby для группировки событий
Работа с API — deque для кэширования запросов
Генерация тестовых данных — product для комбинаций
ETL-процессы — chain и accumulate для потоковой обработки
Оптимизация памяти — итераторы для больших файлов
Домашнее задание: Изучите документацию модулей collections и itertools, попробуйте применить эти
инструменты к собственным данным или задачам.
Спасибо за внимание! Эти инструменты сделают ваш код более эффективным, элегантным и
профессиональным.
English     Русский Rules