functools (lru_cache, partial), itertools, copy (copy/deepcopy)
«Batteries included» — Python поставляется с богатейшей stdlib. Знание её экономит сотни строк кода.
collections — специализированные контейнерыdefaultdict — словарь с дефолтным значениемfrom collections import defaultdict
# Без defaultdict: KeyError или громоздкий setdefault
d = {}
d.setdefault("a", []).append(1)
# С defaultdict: чисто
d = defaultdict(list)
d["a"].append(1)
d["a"].append(2)
d["b"].append(3)
print(dict(d)) # {"a": [1, 2], "b": [3]}
# Граф (список смежности)
graph = defaultdict(set)
graph["Moscow"].add("Saint Petersburg")
graph["Moscow"].add("Kazan")
# Подсчёт
word_count = defaultdict(int)
for word in text.split():
word_count[word] += 1
# defaultdict(lambda: "unknown") — произвольный дефолт
d = defaultdict(lambda: "N/A")
print(d["missing"]) # "N/A"Counter — подсчёт элементовfrom collections import Counter
# Создание
c = Counter("abracadabra")
c = Counter(["a", "b", "a", "c", "a"])
c = Counter({"a": 5, "b": 2})
print(c["a"]) # 5
print(c["z"]) # 0 (не KeyError!)
# Топ N элементов
print(c.most_common(3)) # [("a", 5), ("b", 2), ("r", 2)]
# Арифметика
c1 = Counter(a=3, b=2)
c2 = Counter(a=1, b=4, c=1)
print(c1 + c2) # Counter(b=6, a=4, c=1)
print(c1 - c2) # Counter(a=2) — только положительные
print(c1 & c2) # Counter(a=1, b=2) — минимум
print(c1 | c2) # Counter(b=4, a=3, c=1) — максимум
# total() — Python 3.10+
print(c.total()) # сумма всех значений
# elements() — итерация с повторами
list(Counter("aab").elements()) # ["a", "a", "b"]deque — двусторонняя очередьfrom collections import deque
d = deque([1, 2, 3], maxlen=5)
# Добавление
d.append(4) # добавить справа
d.appendleft(0) # добавить слева
d.extend([5, 6]) # добавить несколько справа
d.extendleft([-1]) # добавить слева (порядок обратный!)
# Удаление
d.pop() # O(1) — удалить справа
d.popleft() # O(1) — удалить слева
# Список: pop(0) это O(n), deque.popleft() это O(1)!
# rotate
d = deque([1, 2, 3, 4, 5])
d.rotate(2) # [4, 5, 1, 2, 3] — вправо
d.rotate(-2) # [1, 2, 3, 4, 5] — влево
# maxlen — автоматически выбрасывает старые элементы
history = deque(maxlen=100)
for event in events:
history.append(event) # старые события удаляются автоматическиnamedtuple — именованный кортежfrom collections import namedtuple
from typing import NamedTuple
# Старый стиль
Point = namedtuple("Point", ["x", "y", "z"])
p = Point(1.0, 2.0, z=3.0)
print(p.x, p[0]) # 1.0 1.0 — и атрибут, и индекс
x, y, z = p # распаковка как tuple
print(p._asdict()) # OrderedDict([('x', 1.0), ...])
# Новый стиль (предпочтительный — типизированный)
class Coordinate(NamedTuple):
x: float
y: float
label: str = ""
c = Coordinate(1.0, 2.0, "origin")
print(c._replace(x=5.0)) # создать копию с изменёнными полями
# Наследование от NamedTuple
class Point3D(NamedTuple):
x: float
y: float
z: float = 0.0
def distance_to_origin(self) -> float:
return (self.x**2 + self.y**2 + self.z**2) ** 0.5ChainMap — цепочка словарейfrom collections import ChainMap
# Поиск идёт по очереди в каждом словаре
defaults = {"color": "blue", "size": "medium"}
config = {"color": "red"}
env = {"debug": True}
merged = ChainMap(env, config, defaults)
print(merged["color"]) # "red" — из config (первый найденный)
print(merged["size"]) # "medium" — из defaults
print(merged["debug"]) # True — из env
# Запись идёт только в первый словарь
merged["new_key"] = "value"
print(env) # {"debug": True, "new_key": "value"}
# Полезно для конфигурации: env vars > config file > defaultsitertools — эффективные итераторыimport itertools# count — бесконечный счётчик
for n in itertools.count(1, 2): # 1, 3, 5, 7...
if n > 10:
break
# cycle — бесконечный цикл по итерируемому
colors = itertools.cycle(["red", "green", "blue"])
for i, color in zip(range(9), colors):
print(f"{i}: {color}") # 0:red, 1:green, 2:blue, 3:red...
# repeat — повторение элемента
list(itertools.repeat(0, 5)) # [0, 0, 0, 0, 0]
# Полезно с map: map(lambda x, y: x**y, values, repeat(2))# product — декартово произведение
list(itertools.product([1, 2], ["a", "b"]))
# [(1,'a'), (1,'b'), (2,'a'), (2,'b')]
# permutations — перестановки (порядок важен)
list(itertools.permutations("ABC", 2))
# [('A','B'), ('A','C'), ('B','A'), ('B','C'), ('C','A'), ('C','B')]
# combinations — комбинации (порядок не важен, без повторов)
list(itertools.combinations([1, 2, 3, 4], 2))
# [(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)]
# combinations_with_replacement — с повторами
list(itertools.combinations_with_replacement("AB", 2))
# [('A','A'), ('A','B'), ('B','B')]# chain — объединение итераторов
list(itertools.chain([1, 2], [3, 4], [5])) # [1, 2, 3, 4, 5]
# chain.from_iterable — "flatten" на один уровень
nested = [[1, 2], [3, 4], [5, 6]]
list(itertools.chain.from_iterable(nested)) # [1, 2, 3, 4, 5, 6]
# islice — срез итератора (не загружает всё в память)
gen = (x**2 for x in itertools.count(1))
list(itertools.islice(gen, 5)) # [1, 4, 9, 16, 25]
list(itertools.islice(gen, 2, 7, 2)) # start=2, stop=7, step=2
# takewhile / dropwhile
nums = [1, 2, 3, 4, 5, 1, 2]
list(itertools.takewhile(lambda x: x < 4, nums)) # [1, 2, 3]
list(itertools.dropwhile(lambda x: x < 4, nums)) # [4, 5, 1, 2]
# groupby — группировка по ключу (входные данные должны быть отсортированы!)
data = sorted([{"name": "Alice", "dept": "IT"},
{"name": "Bob", "dept": "HR"},
{"name": "Charlie", "dept": "IT"}], key=lambda x: x["dept"])
for dept, group in itertools.groupby(data, key=lambda x: x["dept"]):
print(dept, list(group))
# IT [Alice, Charlie]
# HR [Bob]accumulate — накопленные значенияimport itertools, operator
# Накопленная сумма
list(itertools.accumulate([1, 2, 3, 4, 5]))
# [1, 3, 6, 10, 15]
# Накопленное произведение
list(itertools.accumulate([1, 2, 3, 4, 5], operator.mul))
# [1, 2, 6, 24, 120]
# Скользящий максимум
list(itertools.accumulate([3, 1, 4, 1, 5, 9, 2, 6], max))
# [3, 3, 4, 4, 5, 9, 9, 9]functoolsfrom functools import (
lru_cache, cache, cached_property,
partial, partialmethod,
reduce, wraps,
singledispatch, total_ordering
)total_ordering — автогенерация операторов сравненияfrom functools import total_ordering
@total_ordering
class Version:
def __init__(self, major, minor, patch):
self.major, self.minor, self.patch = major, minor, patch
def __eq__(self, other):
return (self.major, self.minor, self.patch) == \
(other.major, other.minor, other.patch)
def __lt__(self, other):
return (self.major, self.minor, self.patch) < \
(other.major, other.minor, other.patch)
# Автоматически генерируются: __le__, __gt__, __ge__
v1 = Version(1, 0, 0)
v2 = Version(2, 0, 0)
print(v1 < v2) # True
print(v2 > v1) # True
print(v1 <= v1) # Truereducefrom functools import reduce
import operator
# Свёртка: (((1+2)+3)+4) = 10
result = reduce(operator.add, [1, 2, 3, 4], 0)
# flatten через reduce + operator.add
nested = [[1, 2], [3, 4], [5]]
flat = reduce(operator.add, nested, []) # [1, 2, 3, 4, 5]
# Но лучше: list(itertools.chain.from_iterable(nested))
# Вычисление факториала
factorial = reduce(operator.mul, range(1, 6), 1) # 120datetime — дата и времяfrom datetime import datetime, date, time, timedelta, timezone
# Создание
now = datetime.now() # локальное время
utc_now = datetime.now(timezone.utc) # UTC время (правильно для хранения!)
d = date(2024, 12, 31)
t = time(23, 59, 59)
dt = datetime(2024, 12, 31, 23, 59, 59, tzinfo=timezone.utc)
# Форматирование
print(now.strftime("%Y-%m-%d %H:%M:%S")) # 2024-03-01 15:30:45
print(now.isoformat()) # 2024-03-01T15:30:45.123456
# Парсинг
dt = datetime.strptime("2024-03-01 15:30", "%Y-%m-%d %H:%M")
dt = datetime.fromisoformat("2024-03-01T15:30:45")
# Арифметика
tomorrow = date.today() + timedelta(days=1)
next_week = datetime.now() + timedelta(weeks=1)
diff = datetime(2024, 12, 31) - datetime.now()
print(diff.days, diff.seconds)
# Unix timestamp
ts = datetime.now().timestamp()
dt = datetime.fromtimestamp(ts)
dt_utc = datetime.fromtimestamp(ts, tz=timezone.utc)
# Сравнение (только одинаковые tzinfo!)
aware = datetime.now(timezone.utc)
naive = datetime.now()
# aware > naive # TypeError! Нельзя сравнивать aware и naivere — регулярные выраженияimport re
text = "Hello, my email is alice@example.com and phone is +7(495)123-45-67"
# Поиск первого совпадения
match = re.search(r"\d+", text)
if match:
print(match.group()) # "7"
print(match.start(), match.end()) # позиции
# Все совпадения
emails = re.findall(r"[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}", text)
# ['alice@example.com']
# Замена
clean = re.sub(r"\s+", " ", " many spaces ") # "many spaces"
clean = re.sub(r"(\w+)@(\w+)", r"\2@\1", text) # группы в замене
# Компиляция (для многократного использования)
EMAIL_RE = re.compile(r"^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$", re.IGNORECASE)
def is_valid_email(email: str) -> bool:
return bool(EMAIL_RE.match(email))
# Именованные группы
DATE_RE = re.compile(r"(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})")
m = DATE_RE.match("2024-03-15")
if m:
print(m.group("year"), m.group("month")) # 2024 03
print(m.groupdict()) # {'year': '2024', 'month': '03', 'day': '15'}
# split по регулярке
parts = re.split(r"[,;\s]+", "one, two; three four")
# ['one', 'two', 'three', 'four']
# Флаги
re.IGNORECASE # (re.I) — без учёта регистра
re.MULTILINE # (re.M) — ^ и $ для каждой строки
re.DOTALL # (re.S) — . совпадает с \n
re.VERBOSE # (re.X) — игнорирует пробелы, разрешает комментарии
VERBOSE_RE = re.compile(r"""
(\d{1,3}) # первая группа цифр
\. # точка
(\d{1,3}) # вторая группа
""", re.VERBOSE)heapq и bisect — эффективные структурыimport heapq
# Min-heap (по умолчанию)
heap = [5, 3, 1, 4, 2]
heapq.heapify(heap) # преобразовать список в heap inplace: [1, 2, ...]
heapq.heappush(heap, 0) # добавить элемент
smallest = heapq.heappop(heap) # O(log n) — извлечь минимум
# N наименьших/наибольших
data = [10, 5, 2, 8, 1, 9, 3]
print(heapq.nsmallest(3, data)) # [1, 2, 3]
print(heapq.nlargest(3, data)) # [10, 9, 8]
# Объединение отсортированных итераторов
sorted_lists = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
merged = list(heapq.merge(*sorted_lists)) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
import bisect
# Бинарный поиск в отсортированном списке
sorted_list = [1, 3, 5, 7, 9]
print(bisect.bisect_left(sorted_list, 5)) # 2 — позиция для вставки слева
print(bisect.bisect_right(sorted_list, 5)) # 3 — позиция для вставки справа
bisect.insort(sorted_list, 4) # O(n) — вставить с сохранением порядка
print(sorted_list) # [1, 3, 4, 5, 7, 9]os и sys — взаимодействие с системойimport os, sys
# Переменные окружения
api_key = os.environ.get("API_KEY", "default")
os.environ["NEW_VAR"] = "value"
# Файловая система (предпочитайте pathlib)
os.getcwd() # текущая директория
os.chdir("/tmp") # изменить
os.makedirs("/tmp/a/b/c", exist_ok=True) # создать дерево
os.listdir(".") # список файлов
os.path.join("a", "b", "c") # соединить путь
# Информация о системе
os.getpid() # PID процесса
os.getppid() # PID родительского процесса
os.cpu_count() # количество ядер
# sys — интерпретатор
sys.version # "3.12.0 (default, ...)"
sys.platform # "linux", "darwin", "win32"
sys.argv # аргументы командной строки
sys.path # пути поиска модулей
sys.exit(0) # завершить программу
sys.stdin, sys.stdout, sys.stderr # стандартные потоки
# Перехват stdout
import io
old_stdout = sys.stdout
sys.stdout = io.StringIO()
print("captured")
output = sys.stdout.getvalue()
sys.stdout = old_stdoutsubprocess — запуск процессовimport subprocess
# Простой запуск (Python 3.7+)
result = subprocess.run(
["ls", "-la"],
capture_output=True, # захватить stdout и stderr
text=True, # декодировать как строку
check=True, # поднять ошибку если returncode != 0
)
print(result.stdout)
print(result.returncode)
# Передача данных через stdin
result = subprocess.run(
["grep", "error"],
input="line1\nerror found\nline3\n",
capture_output=True,
text=True,
)
print(result.stdout) # "error found\n"
# shell=True (осторожно — риск инъекции!)
# subprocess.run(f"cat {filename}", shell=True) # ❌ если filename от пользователя
result = subprocess.run(["cat", filename], ...) # ✅ список аргументов безопасен
# Popen для асинхронного запуска
proc = subprocess.Popen(
["long_running_program"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
# Делаем другую работу...
stdout, stderr = proc.communicate(timeout=30) # ждём завершенияthreading и multiprocessingimport threading
import multiprocessing
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# threading — для I/O-bound задач
def io_task(url):
import requests
return requests.get(url).status_code
urls = ["https://example.com"] * 10
# ThreadPoolExecutor (рекомендуется вместо Thread напрямую)
with ThreadPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(io_task, url) for url in urls]
results = [f.result() for f in futures]
# Или через map:
results = list(executor.map(io_task, urls))
# multiprocessing — для CPU-bound задач
def cpu_task(n):
return sum(i**2 for i in range(n))
with ProcessPoolExecutor(max_workers=4) as executor:
results = list(executor.map(cpu_task, [10**6] * 4))
# Lock для синхронизации
lock = threading.Lock()
data = []
def safe_append(item):
with lock:
data.append(item)
# Event для сигнализации
ready = threading.Event()
def producer():
time.sleep(1)
ready.set()
def consumer():
ready.wait() # блокируется до set()
process()copy — копирование объектовimport copy
original = [[1, 2], [3, 4], [5, 6]]
# Поверхностная копия: новый список, но элементы — те же объекты
shallow = copy.copy(original)
shallow.append([7, 8])
shallow[0].append(99) # Изменяет original[0]!
print(original) # [[1, 2, 99], [3, 4], [5, 6]]
# Глубокая копия: рекурсивно копирует всё
deep = copy.deepcopy(original)
deep[0].append(100) # Не изменяет original
print(original) # [[1, 2, 99], [3, 4], [5, 6]] — не изменилось
# Кастомизация deepcopy через __deepcopy__
class MyClass:
def __deepcopy__(self, memo):
# memo — словарь уже скопированных объектов (избегает бесконечной рекурсии)
new = self.__class__.__new__(self.__class__)
memo[id(self)] = new
for k, v in self.__dict__.items():
setattr(new, k, copy.deepcopy(v, memo))
return newoperator — функциональные операцииimport operator
# Вместо lambda: немного быстрее, более читаемо
operator.add(2, 3) # 5
operator.mul(4, 5) # 20
operator.neg(3) # -3
operator.gt(5, 3) # True
operator.not_(False) # True
operator.contains([1,2,3], 2) # True
# itemgetter — извлечение по индексу/ключу
from operator import itemgetter, attrgetter, methodcaller
data = [("Alice", 30, "IT"), ("Bob", 25, "HR"), ("Charlie", 35, "IT")]
by_age = sorted(data, key=itemgetter(1))
first_and_third = itemgetter(0, 2)(("Alice", 30, "IT")) # ("Alice", "IT")
# attrgetter — извлечение по атрибуту
class Employee:
def __init__(self, name, dept):
self.name = name
self.dept = dept
employees = [Employee("Alice", "IT"), Employee("Bob", "HR")]
by_dept = sorted(employees, key=attrgetter("dept"))
# Вложенные атрибуты
# attrgetter("dept.manager.name")(emp) == emp.dept.manager.name
# methodcaller — вызов метода
transform = methodcaller("upper")
print(list(map(transform, ["hello", "world"]))) # ["HELLO", "WORLD"]
with_args = methodcaller("replace", " ", "_")
print(with_args("hello world")) # "hello_world"itertools.groupby, сгруппируйте список транзакций по дате (строка вида "2024-01-15"). Убедитесь в правильной сортировке.top_words(text, n=10) через Counter.most_common() — топ N слов текста.sliding_window(iterable, size) возвращающий скользящее окно через collections.deque.heapq.nsmallest и heapq.nlargest, найдите k ближайших точек к началу координат из списка (x, y) пар.deep_merge(d1, d2) рекурсивно объединяющую два dict (значения d2 перезаписывают d1, вложенные dict объединяются, не перезаписываются).re, напишите парсер лог-строк формата: "2024-01-15 10:30:45 ERROR module: message".Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.