Модуль `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
Всe інше 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 с
Щоб тренувати сліпий друк, не підглядайте на фізичну клавіатуру.