Python Developer
Interview Guide
Полный справочник для подготовки к техническому собеседованию. Построен на основе реальной оценочной матрицы.
01 Структуры данных
Hash Table
Хэш-таблица — структура данных, обеспечивающая O(1) среднее время доступа по ключу.
Как работает
- Ключ пропускается через хэш-функцию → получаем целое число.
- Число приводится к индексу массива (обычно
hash % capacity). - По индексу хранится бакет (bucket) — слот для одного или нескольких элементов.
Зачем нужны бакеты
Разные ключи могут дать одинаковый индекс — это коллизия. Бакет позволяет хранить несколько пар (key, value) в одном слоте.
| Метод | Описание |
|---|---|
| Chaining (цепочки) | Каждый бакет — связный список / массив. Python dict использует вариацию — open addressing. |
| Open Addressing | При коллизии ищем следующий свободный слот (linear probing, quadratic probing, double hashing). |
В Python dict — open addressing с quadratic probing. Load factor ≤ ⅔ → при превышении таблица расширяется.
Linked List
Связный список — каждый элемент (node) хранит значение и указатель на следующий (Singly) или на оба направления (Doubly).
| Операция | Array | Linked List |
|---|---|---|
| Доступ по индексу | O(1) | O(n) |
| Вставка в начало | O(n) | O(1) |
| Вставка в конец | O(1) amortized | O(1) с tail |
| Удаление по значению | O(n) | O(n) + O(1) |
B-Tree
Сбалансированное дерево для дисковых хранилищ. Каждый узел содержит до M ключей и M+1 потомков. Все листья на одной глубине. Используется в индексах PostgreSQL, файловых системах.
B+ Tree — данные только в листьях, а листья связаны в linked list → быстрый range scan.
Stack vs Queue
| Stack (Стек) | Queue (Очередь) | |
|---|---|---|
| Порядок | LIFO — Last In, First Out | FIFO — First In, First Out |
| Операции | push, pop | enqueue, dequeue |
| Python | list или deque | deque или queue.Queue |
| Применение | DFS, undo, call stack | BFS, планировщик, буферизация |
Графы
Граф = множество вершин (V) + множество рёбер (E). Виды:
- Ориентированный / неориентированный
- Взвешенный / невзвешенный
- Циклический / ациклический (DAG)
# Adjacency List — наиболее частый вариант
graph = {
'A': ['B', 'C'],
'B': ['D'],
'C': ['D'],
'D': []
}
# Adjacency Matrix — O(1) проверка ребра, O(V²) память
02 Алгоритмы
Бинарный поиск
Поиск в отсортированном массиве за O(log n).
def binary_search(arr, target):
lo, hi = 0, len(arr) - 1
while lo <= hi:
mid = (lo + hi) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
lo = mid + 1
else:
hi = mid - 1
return -1
DFS и BFS
| DFS (Depth-First) | BFS (Breadth-First) | |
|---|---|---|
| Структура | Стек (рекурсия или явный) | Очередь |
| Когда | Поиск пути, topological sort, циклы | Кратчайший путь (невзвешенный) |
| Сложность | O(V + E) | O(V + E) |
from collections import deque
def bfs(graph, start):
visited = {start}
queue = deque([start])
while queue:
node = queue.popleft()
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
return visited
def dfs(graph, node, visited=None):
if visited is None:
visited = set()
visited.add(node)
for neighbor in graph[node]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
return visited
Сортировки
| Алгоритм | Среднее | Худшее | Память | Стабильная |
|---|---|---|---|---|
| Bubble Sort | O(n²) | O(n²) | O(1) | Да |
| Insertion Sort | O(n²) | O(n²) | O(1) | Да |
| Merge Sort | O(n log n) | O(n log n) | O(n) | Да |
| Quick Sort | O(n log n) | O(n²) | O(log n) | Нет |
| Timsort (Python) | O(n log n) | O(n log n) | O(n) | Да |
Timsort — гибрид merge sort и insertion sort. Используется в sorted() и list.sort(). Оптимизирован для частично отсортированных данных.
03 Python Core
deep copy vs shallow copy
import copy
a = [[1, 2], [3, 4]]
b = copy.copy(a) # shallow — новый список, но вложенные — те же объекты
c = copy.deepcopy(a) # deep — полная рекурсивная копия
a[0].append(99)
# b[0] == [1, 2, 99] — изменился!
# c[0] == [1, 2] — не изменился
is vs ==
==— сравнение значений (вызывает__eq__)is— сравнение идентичности объектов (id(a) == id(b))
a = [1, 2]; b = [1, 2]
a == b # True — значения равны
a is b # False — разные объекты
a is None # Единственное правильное is-сравнение с None
Float vs Decimal
| float | Decimal | |
|---|---|---|
| Точность | IEEE 754, ~15 цифр, неточен | Произвольная точность |
| Скорость | Быстрый (аппаратный) | ~50-100x медленнее |
| Когда | Научные, ML | Финансы, деньги |
0.1 + 0.2 == 0.3 # False!
from decimal import Decimal
Decimal('0.1') + Decimal('0.2') == Decimal('0.3') # True
Контекстные менеджеры
class ManagedResource:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.close()
return False # True — подавить исключение
# Проще через contextlib
from contextlib import contextmanager
@contextmanager
def managed():
resource = acquire()
try:
yield resource
finally:
resource.release()
__call__
Делает экземпляр класса вызываемым как функция:
class Multiplier:
def __init__(self, factor):
self.factor = factor
def __call__(self, x):
return x * self.factor
double = Multiplier(2)
double(5) # 10
__getattribute__ vs __getattr__
| __getattribute__ | __getattr__ | |
|---|---|---|
| Когда | Всегда при доступе к атрибуту | Только когда атрибут не найден |
| Порядок | Вызывается первым | Fallback |
| Опасность | Бесконечная рекурсия | Безопасен |
class Demo:
x = 10
def __getattribute__(self, name):
print(f"accessing {name}")
return super().__getattribute__(name) # ОБЯЗАТЕЛЬНО super()!
def __getattr__(self, name):
return f"{name} not found"
d = Demo()
d.x # "accessing x" → 10
d.missing # "accessing missing" → "missing not found"
MRO (Method Resolution Order)
Порядок поиска методов при множественном наследовании. Алгоритм — C3 Linearization.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
D.__mro__ # (D, B, C, A, object)
__slots__
Ограничивает атрибуты экземпляра. Нет __dict__ → экономия памяти + быстрый доступ.
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x; self.y = y
p = Point(1, 2)
p.z = 3 # AttributeError!
Замыкания (Closures)
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
c = make_counter()
c() # 1
c() # 2
Protocol и Generics
from typing import Protocol, TypeVar, Generic
class Drawable(Protocol):
def draw(self) -> None: ... # Структурная типизация
T = TypeVar('T')
class Stack(Generic[T]):
def __init__(self): self._items: list[T] = []
def push(self, item: T): self._items.append(item)
def pop(self) -> T: return self._items.pop()
Protocol — structural subtyping. Класс не нужно наследовать — достаточно реализовать нужные методы.
04 ООП, метаклассы, перегрузка
Метаклассы
Метакласс — «класс классов». Класс — экземпляр метакласса. По умолчанию — type.
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
pass
a = Database()
b = Database()
a is b # True
Поток: type.__new__ создаёт класс → type.__init__ инициализирует → type.__call__ создаёт экземпляр.
Перегрузка методов
В Python нет классической перегрузки. Но есть механизмы:
# 1. typing.overload — только для type checkers
from typing import overload
@overload
def process(x: int) -> int: ...
@overload
def process(x: str) -> str: ...
def process(x):
if isinstance(x, int): return x * 2
return x.upper()
# 2. singledispatch — runtime dispatch по типу первого аргумента
from functools import singledispatch
@singledispatch
def handle(arg):
raise NotImplementedError
@handle.register(int)
def _(arg): return arg ** 2
@handle.register(str)
def _(arg): return arg.upper()
Интерфейс vs Абстрактный класс
| ABC | Protocol | |
|---|---|---|
| Типизация | Номинальная (наследование) | Структурная (duck typing) |
| Runtime | Да (isinstance) | Только type checker |
| Реализация | Может содержать | Только сигнатуры |
Идемпотентность и чистая функция
Идемпотентность — повторный вызов с тем же входом даёт тот же эффект. PUT /users/1 — идемпотентен; POST /users — нет.
Чистая функция: (1) при одних аргументах — всегда одинаковый результат; (2) нет побочных эффектов.
# Чистая
def add(a, b): return a + b
# Не чистая
total = 0
def add_to_total(x):
global total
total += x
return total
05 GIL и многопоточность
GIL (Global Interpreter Lock)
Мьютекс CPython — только один поток исполняет Python-байткод одновременно.
- threading — для I/O-bound (GIL отпускается при ожидании I/O)
- multiprocessing — обходит GIL, отдельные процессы
- asyncio — однопоточная конкурентность для I/O
| Задача | Решение |
|---|---|
| I/O-bound (запросы, БД, файлы) | asyncio или threading |
| CPU-bound (вычисления) | multiprocessing или C-расширения |
Python 3.12+: Per-interpreter GIL. Python 3.13+: Экспериментальный free-threaded CPython (без GIL).
06 Django & DRF
select_related vs prefetch_related
| select_related | prefetch_related | |
|---|---|---|
| Как | SQL JOIN — один запрос | Отдельный запрос + Python join |
| Связи | FK, OneToOne | M2M, обратные FK |
# N + 1 проблема:
books = Book.objects.all()
for b in books:
print(b.author.name) # Каждый раз запрос!
# Решение:
books = Book.objects.select_related('author').all() # 1 запрос с JOIN
APIView vs ViewSet
| APIView | ViewSet | |
|---|---|---|
| Контроль | Полный — get, post, put | CRUD через list, create, retrieve... |
| URL | Ручное подключение | Автоматический Router |
| Когда | Нестандартная логика | Стандартный CRUD |
Mixins в DRF
Миксины добавляют CRUD: CreateModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin. Комбинируются с GenericAPIView.
07 FastAPI, WSGI, ASGI
WSGI vs ASGI
| WSGI | ASGI | |
|---|---|---|
| Модель | Синхронная, 1 запрос = 1 поток | Асинхронная, event loop |
| Протоколы | HTTP | HTTP, WebSocket, HTTP/2 |
| Фреймворки | Django (default), Flask | FastAPI, Starlette |
| Серверы | Gunicorn, uWSGI | Uvicorn, Hypercorn |
# WSGI
def app(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return [b'Hello']
# ASGI
async def app(scope, receive, send):
await send({'type': 'http.response.start', 'status': 200})
await send({'type': 'http.response.body', 'body': b'Hello'})
FastAPI: Depends и response_model
from fastapi import FastAPI, Depends
from pydantic import BaseModel
app = FastAPI()
class UserOut(BaseModel):
id: int
name: str
async def get_db():
db = SessionLocal()
try:
yield db # DI + контекстный менеджер
finally:
db.close()
@app.get("/users/{id}", response_model=UserOut)
async def get_user(id: int, db=Depends(get_db)):
return db.query(User).get(id)
08 asyncio
Coroutine vs Task vs Future
| Сущность | Что это | Как создать |
|---|---|---|
| Coroutine | Объект от async def. Не запущен! | coro = my_func() |
| Task | Обёртка, запланирована в event loop | asyncio.create_task(coro) |
| Future | Контейнер для будущего результата | loop.create_future() |
create_task / gather / wait_for
import asyncio
task = asyncio.create_task(fetch_data()) # параллельный запуск
results = await asyncio.gather(a(), b(), c()) # ждём все
try:
r = await asyncio.wait_for(slow(), timeout=5.0) # с таймаутом
except asyncio.TimeoutError: ...
asyncio.run(main()) # точка входа
run_in_executor
async def main():
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, blocking_io) # thread pool
result = await loop.run_in_executor(ProcessPoolExecutor(), cpu) # process pool
Примитивы синхронизации
| Примитив | Описание |
|---|---|
Lock | Только один await за раз |
Event | Сигнализация между корутинами |
Semaphore | Ограничение параллелизма (n одновременно) |
Condition | Lock + Event, ожидание условия |
Queue | Async-очередь producer/consumer |
sem = asyncio.Semaphore(10) # макс 10 одновременно
async def fetch(url):
async with sem:
return await http_client.get(url)
09 Celery
Распределённая очередь задач: Producer → Broker (RabbitMQ / Redis) → Worker → Result Backend.
from celery import Celery
app = Celery('tasks', broker='redis://localhost:6379/0')
@app.task(bind=True, max_retries=3, default_retry_delay=60)
def send_email(self, to, subject, body):
try:
smtp_send(to, subject, body)
except ConnectionError as exc:
raise self.retry(exc=exc)
send_email.delay("user@example.com", "Hi", "Body") # async
send_email.apply_async(args=[...], countdown=300) # через 5 мин
Композиция: chain, group, chord; celery beat — планировщик; acks_late — подтверждение после выполнения.
10 API, REST, RPC
Модель OSI
| # | Уровень | Примеры |
|---|---|---|
| 7 | Application | HTTP, FTP, SMTP, DNS |
| 6 | Presentation | SSL/TLS, JPEG, JSON |
| 5 | Session | Сессии, RPC |
| 4 | Transport | TCP, UDP |
| 3 | Network | IP, ICMP |
| 2 | Data Link | Ethernet, MAC |
| 1 | Physical | Кабели, Wi-Fi |
REST vs RPC
| REST | RPC (gRPC) | |
|---|---|---|
| Ориентация | Ресурсы (/users/1) | Процедуры (GetUser(1)) |
| Протокол | HTTP + JSON | HTTP/2 + Protobuf |
| Стриминг | Нет (WebSocket) | Встроенный bidirectional |
SemVer
MAJOR.MINOR.PATCH: MAJOR — ломающие изменения, MINOR — новая функциональность, PATCH — баг-фиксы.
Circuit Breaker
Защита от каскадных отказов. Три состояния: Closed (нормально, считаем ошибки) → Open (fail fast, всё отклоняется) → Half-Open (пробные запросы).
Stateless vs Stateful
- Stateless — сервер не хранит состояние между запросами (REST, JWT). Легко масштабируется.
- Stateful — сервер помнит клиента (WebSocket, серверные сессии).
Monkey Patching
Динамическая замена атрибутов/методов в runtime:
some_module.original_function = patched_function # Monkey patch
# В тестах — pytest monkeypatch:
def test_something(monkeypatch):
monkeypatch.setattr(requests, 'get', mock_get)
Применение: тестирование, gevent/eventlet. Опасно в продакшене.
11 HTTP & RESTful
HTTP-методы
| Метод | Идемпотентный | Safe | Тело |
|---|---|---|---|
| GET | Да | Да | Нет |
| POST | Нет | Нет | Да |
| PUT | Да | Нет | Да |
| PATCH | Не гарантирован | Нет | Да |
| DELETE | Да | Нет | Опционально |
Коды ответов
- 2xx — успех: 200 OK, 201 Created, 204 No Content
- 3xx — перенаправление: 301, 304
- 4xx — ошибка клиента: 400, 401, 403, 404, 409, 429
- 5xx — ошибка сервера: 500, 502, 503
12 Теория РСУБД
Проблема N + 1
1 запрос для списка + N запросов для связанных данных каждого элемента. Решение: JOIN, select_related, joinedload.
ACID
| Свойство | Описание |
|---|---|
| Atomicity | Транзакция выполняется полностью или не выполняется |
| Consistency | БД из одного валидного состояния в другое |
| Isolation | Параллельные транзакции не влияют друг на друга |
| Durability | Закоммиченные данные сохраняются при сбое |
Уровни изоляции
| Уровень | Dirty Read | Non-Repeatable | Phantom |
|---|---|---|---|
| Read Uncommitted | Да | Да | Да |
| Read Committed (PG default) | Нет | Да | Да |
| Repeatable Read | Нет | Нет | Да* |
| Serializable | Нет | Нет | Нет |
13 PostgreSQL
Индексы
| Тип | Когда использовать |
|---|---|
| B-tree (default) | =, <, >, BETWEEN, ORDER BY |
| Hash | Только равенство |
| GiST | Полнотекст, гео, ranges |
| GIN | JSONB, массивы, fulltext |
| BRIN | Огромные таблицы с физически упорядоченными данными |
EXPLAIN vs EXPLAIN ANALYZE
| EXPLAIN | EXPLAIN ANALYZE | |
|---|---|---|
| Что делает | Показывает план (оценки) | Выполняет + реальные метрики |
| Запрос выполняется? | Нет | Да |
-- Seq Scan — полное сканирование (медленно)
-- Index Scan — через индекс (быстро)
-- Index Only Scan — данные из индекса (ещё быстрее)
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'a@b.com';
-- Если Seq Scan — нужен индекс!
Shared Lock vs Exclusive Lock
| Shared (FOR SHARE) | Exclusive (FOR UPDATE) | |
|---|---|---|
| Чтение | Разрешено всем | Разрешено |
| Запись | Блокирована | Блокирована для других |
Deadlock
Deadlock — взаимная блокировка: Tx1 ждёт ресурс Tx2, и наоборот.
-- Tx1: UPDATE accounts SET balance=100 WHERE id=1; -- блокирует 1
-- Tx2: UPDATE accounts SET balance=200 WHERE id=2; -- блокирует 2
-- Tx1: UPDATE accounts SET balance=300 WHERE id=2; -- ждёт 2
-- Tx2: UPDATE accounts SET balance=400 WHERE id=1; -- ждёт 1 → DEADLOCK!
PG автоматически обнаруживает и откатывает одну транзакцию. Предотвращение: блокировать в одинаковом порядке; NOWAIT; SKIP LOCKED.
Constraints
PRIMARY KEY,FOREIGN KEY,UNIQUE,CHECK,NOT NULL,EXCLUDE
Views
-- Обычная View
CREATE VIEW active_users AS
SELECT * FROM users WHERE is_active = true;
-- Materialized View — кэшированный результат
CREATE MATERIALIZED VIEW monthly_stats AS
SELECT date_trunc('month', created_at) as month, count(*)
FROM orders GROUP BY 1;
REFRESH MATERIALIZED VIEW monthly_stats;
Репликация / Шардирование / Партиционирование
| Репликация | Шардирование | Партиционирование | |
|---|---|---|---|
| Что | Копии на другие серверы | Разделение между серверами | Разделение внутри сервера |
| Зачем | Отказоустойчивость, read scaling | Горизонтальное масштабирование | Производительность запросов |
14 NoSQL & Redis
Типы NoSQL
| Тип | Примеры | Когда |
|---|---|---|
| Document | MongoDB | Гибкая схема, вложенные документы |
| Key-Value | Redis, DynamoDB | Кэш, сессии |
| Column-family | ClickHouse, Cassandra | Аналитика, OLAP |
| Graph | Neo4j | Связи, рекомендации |
Redis
In-memory store. Структуры: String, Hash, List, Set, Sorted Set, Stream.
Применения
- Кэш — с TTL (
SET key value EX 3600) - Брокер — Pub/Sub, Streams, Celery broker
- Сессии — быстрое чтение/запись + автоистечение
- Rate limiting — INCR + EXPIRE
- Distributed lock — SETNX + TTL (Redlock)
Eviction: noeviction, allkeys-lru, volatile-lru, allkeys-lfu, volatile-ttl.
15 Брокеры сообщений
Queue vs Pub/Sub vs Topic
| Паттерн | Описание | Доставка |
|---|---|---|
| Queue | Point-to-point: один consumer | Competing consumers |
| Pub/Sub | Broadcast: все подписчики | Fan-out |
| Topic | Pub/Sub с фильтрацией | По ключу/паттерну |
Гарантии доставки
| Гарантия | Описание |
|---|---|
| At most once | Может потеряться, не дублируется |
| At least once | Минимум раз, возможны дубли |
| Exactly once | Ровно раз (очень сложно) |
16 RabbitMQ
Виды Exchange
| Type | Routing | Пример |
|---|---|---|
| Direct | Точное совпадение routing key | key="order.created" |
| Fanout | Во все очереди (broadcast) | Уведомления |
| Topic | Паттерны (*, #) | order.* |
| Headers | По заголовкам сообщения | Сложная маршрутизация |
17 Kafka
Распределённый лог (append-only): Producer → Topic (Partitions) → Consumer Group.
- Partition — упорядоченный лог. Параллелизм = количество партиций.
- Offset — позиция сообщения. Consumer отслеживает свой offset.
- Consumer Group — каждая партиция назначена одному consumer в группе.
- Commit offset — подтверждение обработки: auto или ручной.
- Replication Factor — количество копий (отказоустойчивость).
18 System Design
C4 Model
- Context — система + окружение (пользователи, внешние системы)
- Container — развёртываемые единицы (API, БД, frontend)
- Component — модули внутри контейнера
- Code — классы, интерфейсы
Микросервисы vs Монолит
| Монолит | Микросервисы | |
|---|---|---|
| Деплой | Всё вместе | Независимо |
| Сложность | Код ↑, инфра ↓ | Код ↓, инфра ↑ |
| Когда | Старт, маленькая команда | Большие команды, домены |
19 Паттерны проектирования
Singleton
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
Adapter
Оборачивает объект с несовместимым интерфейсом:
class NewPaymentAdapter:
def __init__(self, old: OldPayment):
self._old = old
def pay(self, amount):
return self._old.make_payment(amount)
Facade
Простой интерфейс к сложной подсистеме. Скрывает внутреннюю сложность за одним методом.
Observer vs Mediator
| Observer | Mediator | |
|---|---|---|
| Связь | Один-ко-многим | Многие-ко-многим через посредника |
| Знание | Subject знает об observers | Компоненты знают только mediator |
| Пример | Django signals, event listeners | Чат-комната, диспетчер |
class EventEmitter:
def __init__(self):
self._listeners = {}
def on(self, event, callback):
self._listeners.setdefault(event, []).append(callback)
def emit(self, event, *args):
for cb in self._listeners.get(event, []):
cb(*args)
20 SOLID & Принципы
SOLID
| Принцип | Простыми словами | |
|---|---|---|
| S | Single Responsibility | Одна причина для изменения |
| O | Open/Closed | Открыт для расширения, закрыт для модификации |
| L | Liskov Substitution | Подтип заменяем базовым без поломки |
| I | Interface Segregation | Много маленьких интерфейсов лучше одного большого |
| D | Dependency Inversion | Зависимость от абстракций, не от реализаций |
Примеры SOLID на Python
# S — Single Responsibility
# Плохо: класс и сохраняет, и отправляет email
# Хорошо: отдельный UserRepository и EmailService
# O — Open/Closed
class Discount(ABC):
@abstractmethod
def calculate(self, price): ...
class SeasonalDiscount(Discount):
def calculate(self, price): return price * 0.8
# L — Liskov Substitution
# Bird.fly() не должен бросать NotImplementedError для Penguin
# I — Interface Segregation
class Readable(Protocol):
def read(self) -> str: ...
class Writable(Protocol):
def write(self, data: str): ...
# D — Dependency Inversion
class OrderService:
def __init__(self, repo: OrderRepository): # абстракция
self.repo = repo
DRY, KISS, YAGNI
- DRY — Don't Repeat Yourself
- KISS — Keep It Simple, Stupid
- YAGNI — You Aren't Gonna Need It
Low Coupling / High Cohesion
- Low Coupling — модули минимально зависят друг от друга
- High Cohesion — элементы внутри модуля тесно связаны по функции
21 Docker & Kubernetes
Docker
- Image — неизменяемый шаблон (слои)
- Container — запущенный экземпляр
- Volume — персистентное хранилище
- Network — виртуальная сеть
# Multi-stage build
FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /usr/local/lib/python3.12/site-packages \
/usr/local/lib/python3.12/site-packages
COPY . .
RUN useradd -m appuser
USER appuser
CMD ["uvicorn", "main:app", "--host", "0.0.0.0"]
Docker Compose
services:
api:
build: .
ports: ["8000:8000"]
depends_on: [db, redis]
db:
image: postgres:16
volumes: [pgdata:/var/lib/postgresql/data]
redis:
image: redis:7-alpine
volumes:
pgdata:
Kubernetes
| Ресурс | Описание |
|---|---|
| Pod | Минимальная единица деплоя |
| Deployment | Rolling updates, ReplicaSet |
| Service | Стабильный endpoint |
| ConfigMap / Secret | Конфигурация / секреты |
| Ingress | HTTP routing, TLS |
| HPA | Автоскейлинг по нагрузке |
22 Тестирование
Виды тестов
- Unit — изолированная единица (функция, метод)
- Integration — взаимодействие компонентов
- E2E — полный пользовательский сценарий
pytest: fixtures и mocks
import pytest
from unittest.mock import patch
@pytest.fixture
def db_session():
session = create_session()
yield session
session.rollback()
@pytest.mark.parametrize("input,expected", [(1,2), (2,4)])
def test_double(input, expected):
assert double(input) == expected
@patch("app.services.send_email")
def test_registration(mock_send):
mock_send.return_value = True
result = register_user("user@test.com")
assert result.success
mock_send.assert_called_once()
23 Безопасность
OWASP Top 10
| Угроза | Защита |
|---|---|
| SQL Injection | Параметризованные запросы, ORM |
| XSS | Экранирование, CSP |
| CSRF | CSRF-токены, SameSite cookies |
| Broken Auth | JWT + refresh, bcrypt, MFA |
JWT
header.payload.signature. Stateless auth. Access token (короткий TTL) + Refresh token (длинный, httpOnly cookie).
24 UNIX & Git
Linux
ps aux,htop— процессыgrep,awk,sed— обработка текстаlsof -i :8000— кто слушает портss/netstat— сетевые соединенияchmod,chown— праваsystemctl,journalctl— сервисы и логи
Git (Senior level)
rebasevsmerge— rebase переписывает историю (линейная), merge создаёт merge-commitcherry-pick— перенос одного коммитаbisect— бинарный поиск коммита с багомstash— временное сохранениеreflog— история всех действий (спасение)- Стратегии: Git Flow, Trunk-Based, GitHub Flow