Фундамент: углубленный Python, принципы ООП и паттерны проектирования
Фундамент: углубленный Python, принципы ООП и паттерны проектирования
Приветствую на курсе. Твоя цель — стать Team Lead в кратчайшие сроки. Это амбициозная задача, требующая смены мышления. Если Junior думает «как заставить это работать», то Team Lead думает «как это будет жить, масштабироваться и поддерживаться через год».
В этой статье мы укрепим твой технический фундамент. Мы не будем учить синтаксис if-else, мы разберем то, на чем сыпятся на собеседованиях и в архитектуре реальных проектов.
Углубленный Python: что под капотом?
Чтобы управлять командой Python-разработчиков, ты должен понимать язык глубже, чем просто написание скриптов. Ошибки в понимании работы памяти и структур данных приводят к «плавающим» багам, которые убивают недели разработки.
Управление памятью и изменяемость
В Python все является объектом. Но ключевое различие, которое ты обязан помнить, — это mutable (изменяемые) и immutable (неизменяемые) типы данных.
- Immutable:
int,float,str,tuple,frozenset. При попытке изменения создается новый объект. - Mutable:
list,dict,set, пользовательские классы. Изменяются in-place (на месте).
Классическая ловушка, которую ты будешь ловить на код-ревью у своих джунов — изменяемые аргументы по умолчанию.
# ПЛОХО: список создается один раз при определении функции
def add_item(item, items=[]):
items.append(item)
return items
# ХОРОШО: список создается каждый раз при вызове
def add_item_correct(item, items=None):
if items is None:
items = []
items.append(item)
return items
Алгоритмическая сложность структур данных
Тимлид отвечает за производительность. Ты должен интуитивно понимать, когда использовать list, а когда set. Это описывается через «O-большое».
Поиск элемента в списке (list) выполняется перебором, поэтому его сложность:
где — количество элементов в списке, а — верхняя граница времени выполнения. Чем больше список, тем дольше поиск.
Поиск в хеш-таблице (set или dict) работает иначе:
где означает константное время. Время поиска практически не зависит от количества элементов (при отсутствии коллизий).
«Преждевременная оптимизация — корень всех зол, но выбор неправильной структуры данных — это не оптимизация, а ошибка проектирования».
Объектно-Ориентированное Программирование (ООП)
Ты наверняка знаешь про инкапсуляцию, наследование и полиморфизм. Давай копнем глубже, туда, где возникают реальные проблемы архитектуры.
MRO (Method Resolution Order)
В Python поддерживается множественное наследование. Главная проблема здесь — «Ромбовидное наследование» (Diamond Problem). Как Python понимает, чей метод вызывать, если класс наследуется от двух родителей, имеющих общего предка?
Python использует алгоритм линеаризации C3. Ты можешь посмотреть порядок разрешения методов через атрибут __mro__.
class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass
print(D.__mro__)
# (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
Важно: Если ты строишь сложную иерархию наследования, ты, скорее всего, делаешь что-то не так. Предпочитай композицию наследованию.
SOLID принципы
Это библия для проектирования гибких систем. Как Тимлид, ты будешь использовать их для аргументации на код-ревью.
- S — SRP (Single Responsibility Principle): У класса должна быть только одна причина для изменения. Не смешивай логику сохранения в БД и отправку email в одном классе.
- O — OCP (Open/Closed Principle): Программные сущности должны быть открыты для расширения, но закрыты для модификации. Используй наследование или стратегии, чтобы менять поведение, не переписывая старый код.
- L — LSP (Liskov Substitution Principle): Наследники должны безболезненно заменять родителей. Если ты наследуешься от класса
Bird, но твойPenguinкидает ошибку в методеfly(), ты нарушил LSP. - I — ISP (Interface Segregation Principle): Много специализированных интерфейсов лучше, чем один универсальный. В Python это реализуется через абстрактные базовые классы (
abc). - D — DIP (Dependency Inversion Principle): Зависимости должны строиться на абстракциях, а не на деталях. Модули верхних уровней не должны зависеть от модулей нижних уровней.
Паттерны проектирования
Не нужно зубрить все 23 паттерна GoF. В Python многие из них встроены в язык (например, Итератор или Декоратор). Но есть те, которые критически важны для структуры backend-приложений.
Singleton (Одиночка)
Гарантирует, что у класса есть только один экземпляр. Часто используется для подключений к БД или логгеров.
Нюанс Python: Модуль в Python — это уже Singleton. Если тебе нужен глобальный объект, просто создай его в модуле и импортируй.
Factory Method (Фабричный метод)
Позволяет создавать объекты, не указывая точный класс создаваемого объекта. Полезно, когда логика создания сложная или зависит от конфигурации.
Strategy (Стратегия)
Позволяет выбирать алгоритм поведения на лету. В Python это часто реализуется просто передачей функции как аргумента.
# Пример паттерна Стратегия через функции
def pay_by_card(amount):
print(f"Оплата картой: {amount}")
def pay_by_crypto(amount):
print(f"Оплата криптой: {amount}")
def process_payment(amount, strategy):
strategy(amount)
process_payment(100, pay_by_card)
Архитектура и структура проекта
Тимлид задает стандарты. Если каждый разработчик будет создавать папки как хочет, проект превратится в хаос.
Стандартная структура
Для большинства веб-сервисов (Django/FastAPI/Flask) придерживайся такой структуры:
src/(или название проекта) — исходный код.core/— базовые настройки, конфиги, исключения.api/— ручки (endpoints), схемы данных (Pydantic).services/— бизнес-логика.db/— модели БД и миграции.utils/— вспомогательные функции.
tests/— тесты (зеркалят структуруsrc).docs/— документация..env.example— пример переменных окружения.requirements.txt/pyproject.toml— зависимости.Dockerfile/docker-compose.yml— контейнеризация.
Чистая Архитектура (Clean Architecture)
Чтобы проект жил долго, разделяй его на слои. Зависимости должны идти строго снаружи внутрь.
- Domain (Сущности): Самая важная бизнес-логика. Не зависит ни от чего (ни от БД, ни от фреймворка).
- Application (Use Cases): Сценарии работы приложения. Оперирует сущностями.
- Infrastructure: Реализация работы с БД, внешними API, отправка писем.
Соблюдение этого правила позволяет тебе легко сменить базу данных с PostgreSQL на MongoDB или фреймворк с Flask на FastAPI, не переписывая бизнес-логику.
Заключение
Твоя задача как Тимлида — не писать код быстрее всех, а создавать среду, где код пишется качественно и предсказуемо. Знание того, как работает память, понимание сложности алгоритмов ( vs ), соблюдение SOLID и чистая архитектура — это инструменты, которые позволят тебе строить надежные системы.
В следующей статье мы перейдем к процессам разработки: Git Flow, Code Review и CI/CD.