Модуль `collections` предоставляет специализированные контейнерные типы, решающие распространённые задачи чище, чем обычные dict и list.
**Counter — подсчёт вхождений**
```python
from collections import Counter
words = ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple']
c = Counter(words)
# Counter({'apple': 3, 'banana': 2, 'cherry': 1})
c['apple'] # 3
c['mango'] # 0 — отсутствующие ключи возвращают 0, не KeyError
c.most_common(2) # [('apple', 3), ('banana', 2)]
c.total() # 6 (Python 3.10+)
# Подсчёт символов в строке
Counter('mississippi') # Counter({'s': 4, 'i': 4, 'p': 2, 'm': 1})
```
**Арифметика Counter**
```python
a = Counter({'cat': 3, 'dog': 2})
b = Counter({'dog': 1, 'bird': 2})
a + b # Counter({'cat': 3, 'dog': 3, 'bird': 2}) — объединение (сумма)
a - b # Counter({'cat': 3, 'dog': 1}) — разность (убирает отрицательные)
a & b # Counter({'dog': 1}) — пересечение (минимум)
a | b # Counter({'cat': 3, 'bird': 2, 'dog': 2}) — объединение (максимум)
```
**defaultdict — автоматические значения по умолчанию**
`defaultdict` вызывает фабричную функцию для создания отсутствующих значений вместо `KeyError`:
```python
from collections import defaultdict
# Группировка слов по первой букве
by_letter = defaultdict(list) # фабрика: list -> по умолчанию []
for word in ['apple', 'avocado', 'banana', 'blueberry']:
by_letter[word[0]].append(word)
# defaultdict({'a': ['apple', 'avocado'], 'b': ['banana', 'blueberry']})
# Подсчёт без Counter
freq = defaultdict(int) # фабрика: int -> по умолчанию 0
for ch in 'hello world':
freq[ch] += 1
# Вложенные словари
graph = defaultdict(set) # список смежности для графа
graph['A'].add('B')
graph['A'].add('C')
```
**defaultdict vs dict.get vs dict.setdefault**
```python
d = {}
# Обычный dict — три подхода к отсутствующим ключам:
d.get('key', 0) + 1 # чтение с дефолтом, но не сохраняет его
d.setdefault('key', []).append(1) # сохраняет дефолт при первом доступе
# defaultdict — чище всего, когда все отсутствующие ключи одного типа
dd = defaultdict(list)
dd['key'].append(1) # не нужна специальная обработка
```
**deque — двусторонняя очередь**
`deque` (произносится 'дек') поддерживает O(1) добавление и удаление с обоих концов. В обычном списке `insert(0, x)` и `pop(0)` — O(n), т.к. все элементы сдвигаются.
```python
from collections import deque
d = deque([1, 2, 3])
d.append(4) # [1, 2, 3, 4] — добавить справа
d.appendleft(0) # [0, 1, 2, 3, 4] — добавить слева, O(1)
d.pop() # 4, d = [0, 1, 2, 3]
d.popleft() # 0, d = [1, 2, 3]
d.rotate(1) # [3, 1, 2] — вращение вправо на 1
d.rotate(-1) # [1, 2, 3] — вращение влево на 1
```
**maxlen — скользящее окно / буфер фиксированного размера**
Когда у `deque` есть `maxlen`, новые элементы автоматически вытесняют старые с другого конца:
```python
# Хранить последние 5 введённых команд
history = deque(maxlen=5)
for cmd in ['ls', 'cd /tmp', 'cat file', 'pwd', 'ls -la', 'whoami']:
history.append(cmd)
list(history) # ['cd /tmp', 'cat file', 'pwd', 'ls -la', 'whoami']
# 'ls' был вытеснен, когда пришёл 'whoami', потому что maxlen=5
# Скользящее среднее
window = deque(maxlen=3)
for reading in [10, 20, 30, 40, 50]:
window.append(reading)
print(sum(window) / len(window)) # 10.0, 15.0, 20.0, 30.0, 40.0
```
**namedtuple — лёгкий тип записи**
```python
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
p.x # 3 — доступ по атрибуту
p[0] # 3 — доступ по индексу (это всё ещё кортеж)
p.x, p.y # 3, 4
x, y = p # распаковка тоже работает
```
**Полезные методы namedtuple**
```python
Point._fields # ('x', 'y')
Point._make([3, 4]) # Point(x=3, y=4) из любого итерируемого
p._replace(x=10) # Point(x=10, y=4) — возвращает новый экземпляр
p._asdict() # {'x': 3, 'y': 4}
```
**namedtuple vs dict vs dataclass**
```
namedtuple — неизменяемый, совместимый с кортежем, очень экономный по памяти
dict — изменяемый, гибкие ключи, чуть больше памяти
dataclass — изменяемый по умолчанию, поддерживает методы, type hints, __post_init__
```
Если поля фиксированы и мутация не нужна, `namedtuple` — самый лёгкий выбор.
OrderedDict, ChainMap и выбор правильной коллекции
**OrderedDict — словарь, запоминающий порядок вставки**
В Python 3.7+ обычный `dict` тоже сохраняет порядок вставки, поэтому `OrderedDict` редко нужен. Он полезен для метода `.move_to_end()` и когда хочешь явно обозначить, что порядок важен:
```python
from collections import OrderedDict
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od.move_to_end('a') # переместить 'a' в конец
list(od) # ['b', 'c', 'a']
od.move_to_end('c', last=False) # переместить 'c' в начало
list(od) # ['c', 'b', 'a']
# LRU-кеш (вытеснение давно неиспользуемого)
class LRUCache:
def __init__(self, capacity):
self.cache = OrderedDict()
self.cap = capacity
def get(self, key):
if key not in self.cache: return -1
self.cache.move_to_end(key) # пометить как недавно использованный
return self.cache[key]
def put(self, key, value):
self.cache[key] = value
self.cache.move_to_end(key)
if len(self.cache) > self.cap:
self.cache.popitem(last=False) # вытеснить самый старый
```
**Выбор правильной коллекции**
```
Потребность Используй
──────────────────────────────────── ─────────────────────
Подсчёт вхождений Counter
Группировка, авто-инит отсутствующих ключей defaultdict(list)
Накопление без KeyError для новых ключей defaultdict(int/float)
Быстрая очередь (оба конца) deque
Скользящее окно фиксированного размера deque(maxlen=N)
Лёгкая неизменяемая запись namedtuple
Изменяемая запись с методами dataclass
Словарь с явным управлением порядком OrderedDict
Всё остальное dict / list
```
**ChainMap — объединение нескольких словарей без копирования**
```python
from collections import ChainMap
defaults = {'color': 'red', 'size': 'M'}
overrides = {'color': 'blue'}
merged = ChainMap(overrides, defaults)
merged['color'] # 'blue' — найдено в overrides первым
merged['size'] # 'M' — проваливается до defaults
# Записи идут в первый словарь
merged['weight'] = 'heavy'
overrides # {'color': 'blue', 'weight': 'heavy'}
```
Полезен для слоёной конфигурации (настройки пользователя перекрывают умолчания) и для поиска переменных по областям видимости.
Сгруппируйте список слов по первой букве с помощью `defaultdict`. Верните словарь, где каждый ключ — буква, а значение — список слов.
from collections import defaultdict
def group_by_letter(words):
# ваш код здесь
pass
words = ['apple', 'banana', 'avocado', 'blueberry', 'cherry']
result = group_by_letter(words)
print(result['a']) # ['apple', 'avocado']
print(result['b']) # ['banana', 'blueberry']
Решение
from collections import defaultdict
def group_by_letter(words):
groups = defaultdict(list)
for word in words:
groups[word[0]].append(word)
return dict(groups)
Используйте `OrderedDict` для создания кэша, хранящего последние 3 уникальных добавленных элемента (LRU-подобный). Реализуйте методы `add(key, value)` и `get_all()`.
from collections import OrderedDict
class SmallCache:
def __init__(self):
self.cache = OrderedDict()
self.max_size = 3
def add(self, key, value):
# ваш код здесь
pass
def get_all(self):
return list(self.cache.items())
c = SmallCache()
c.add('a', 1)
c.add('b', 2)
c.add('c', 3)
c.add('d', 4) # 'a' должен быть вытеснен
print(c.get_all()) # [('b', 2), ('c', 3), ('d', 4)]
Решение
from collections import OrderedDict
class SmallCache:
def __init__(self):
self.cache = OrderedDict()
self.max_size = 3
def add(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.max_size:
self.cache.popitem(last=False) # удалить самый старый
def get_all(self):
return list(self.cache.items())
Используйте `deque` для реализации скользящего максимума окна: для списка чисел и размера окна k верните список максимального значения в каждом окне.
from collections import deque
def sliding_max(nums, k):
# ваш код здесь
pass
print(sliding_max([1, 3, -1, -3, 5, 3, 6, 7], 3))
# [3, 3, 5, 5, 6, 7]
Решение
from collections import deque
def sliding_max(nums, k):
result = []
window = deque() # хранит индексы
for i, n in enumerate(nums):
while window and nums[window[-1]] <= n:
window.pop()
window.append(i)
if window[0] <= i - k:
window.popleft()
if i >= k - 1:
result.append(nums[window[0]])
return result
Используйте `namedtuple` для создания типа `Point` с полями `x` и `y`. Верните функцию, вычисляющую расстояние между двумя точками.
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
def distance(p1, p2):
# ваш код здесь
pass
a = Point(0, 0)
b = Point(3, 4)
print(distance(a, b)) # 5.0
Решение
from collections import namedtuple
import math
Point = namedtuple('Point', ['x', 'y'])
def distance(p1, p2):
return math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2)
Используйте `deque` с `maxlen` для хранения только последних N элементов, добавленных в поток. Реализуйте класс `StreamBuffer` с методами `push(item)` и `get_recent()`.
from collections import deque
class StreamBuffer:
def __init__(self, maxlen):
# ваш код здесь
pass
def push(self, item):
# ваш код здесь
pass
def get_recent(self):
return list(self.buffer)
buf = StreamBuffer(3)
for x in [1, 2, 3, 4, 5]:
buf.push(x)
print(buf.get_recent()) # [3, 4, 5]
Решение
from collections import deque
class StreamBuffer:
def __init__(self, maxlen):
self.buffer = deque(maxlen=maxlen)
def push(self, item):
self.buffer.append(item)
def get_recent(self):
return list(self.buffer)
from collections import defaultdict
def totals_by_category(transactions):
totals = defaultdict(float)
for t in transactions:
totals[t['category']] += t['amount']
return dict(totals)
No split tab
Настройки cookies
Мы используем необходимые cookies для работы сайта. С вашего разрешения мы также можем сохранять настройки сайта и использовать аналитические и рекламные cookies, чтобы понимать использование сайта и поддерживать развитие проекта.
* Вы всегда можете изменить свой выбор в настройках сайта.
Выберите категории cookies
Настройки аналитики
Можно отключить аналитику использования платформы. Также можно отправить в Google Analytics запрос на удаление данных об использовании этого сайта, связанных с этим браузером.
Учебный workspace
Учитесь, читая, запуская код и решая задачи.
Практикуйте программирование с пояснениями тем, упражнениями, инструментами browser IDE, проверкой regex и тренировкой печати кода в одном workspace.
Открывайте инструменты во вкладках.Упражнения, IDE-инструменты и тренажеры остаются доступными как вкладки сайта.
Переключайтесь без потери контекста.Переходите между пояснениями, кодом и утилитами, сохраняя свое место.
Используйте sidebar как карту.Левые панели содержат навигацию, настройки, файлы, libraries и управление инструментами.
PythonJavaScriptSQLite
Одна IDE, три практичных режима
Python в браузере.Запускайте небольшие скрипты, пробуйте библиотеки и тренируйте API-запросы без установки.
JavaScript для быстрых экспериментов.Проверяйте код для браузера и сравнивайте идеи рядом с учебными материалами.
SQLite для практики с данными.Открывайте обозреватель базы данных, изучайте таблицы, пишите запросы и учитесь SQL локально.
ТемаIDE
Работайте рядом в split tabs
Держите инструкции перед глазами.Откройте упражнение или справочную страницу рядом с IDE, вместо постоянных переключений.
Сравнивайте инструменты во время обучения.Размещайте проверки regex, пояснения и эксперименты с кодом рядом, когда это нужно для задачи.
Закройте split, когда закончите.Workspace вернётся к одной сфокусированной вкладке, а открытые вкладки сайта останутся доступны.
Тренажер слепой печати кода
Или просто текста
Тренажер рассчитан на физическую клавиатуру.Откройте этот раздел на ноутбуке или компьютере с широким экраном. На телефоне тренировка слепой печати не будет корректной.
Скорость: 0 зн/мин
0 слов/мин
Лучшая скорость (60с): 0 зн/мин
0 слов/мин
Ошибки: 0
Общее время: 0.0 с
Для активации режима слепого набора не подсматривайте на физическую клавиатуру.