SprintCode.pro

Подготовка к алгоритмическим задачам

Super

Собеседование на Python-разработчика: полное руководство

25 мин чтения
python
interview
algorithms
data structures
backend

Почему подготовка к собеседованию на позицию Python-разработчика важна?

Собеседование на позицию Python-разработчика позволяет оценить:

  • 🧠 Глубину понимания языка Python и его экосистемы
  • 🔧 Практические навыки программирования и алгоритмического мышления
  • 🏗️ Знание фреймворков, библиотек и инструментов
  • 📊 Умение работать с данными и оптимизировать код
  • 🌟 Понимание лучших практик разработки ПО

Структура типичного собеседования

Общий план:

  1. Теоретические вопросы (основы Python, ООП, функциональное программирование)
  2. Технические вопросы по фреймворкам и библиотекам (Django, Flask, FastAPI)
  3. Алгоритмические задачи и структуры данных
  4. Практические задания (живое кодирование, код-ревью)
  5. Проектные вопросы (архитектура, производительность, безопасность)

Ключевые области и вопросы

1. Основы Python и его особенности

Типичные вопросы:

ВопросНа что обратить внимание
Что такое PEP 8?Знание стилистических конвенций Python
Отличия Python 2 от Python 3Ключевые различия, unicode, print как функция
Типизация в PythonДинамическая типизация, type hints, mypy
Как работает GIL?Влияние на многопоточность, альтернативы
Что такое итераторы и генераторы?Протокол итерации, yield, эффективность по памяти

Пример задания: Реализовать генератор рендеринга чисел Фибоначчи

def fibonacci_generator(n): """ Генератор для получения первых n чисел Фибоначчи. Args: n: Количество чисел Фибоначчи для генерации Yields: Следующее число Фибоначчи в последовательности """ a, b = 0, 1 for _ in range(n): yield a a, b = b, a + b # Использование генератора for num in fibonacci_generator(10): print(num) # Вывод: 0 1 1 2 3 5 8 13 21 34

2. Структуры данных и их реализация в Python

Типичные вопросы:

ВопросНа что обратить внимание
Отличия list, tuple, set, dictМутабельность, производительность, хеширование
Как работают словари в Python?Хеш-таблицы, коллизии, порядок элементов
Что такое collections в Python?namedtuple, defaultdict, Counter, deque
Как реализованы списки?Динамический массив, операции сложности
Время выполнения операций для разных структурBig O для основных операций

Пример задания: Реализовать LRU-кэш с заданным размером

from collections import OrderedDict class LRUCache: """ Реализация LRU (Least Recently Used) кэша фиксированного размера. Attributes: capacity: Максимальное количество элементов в кэше cache: OrderedDict для хранения элементов """ def __init__(self, capacity): self.capacity = capacity self.cache = OrderedDict() def get(self, key): """ Получение значения из кэша по ключу. Если ключ найден, перемещаем его в конец (недавно использованный). Args: key: Ключ для поиска Returns: Значение по ключу или -1, если ключ не найден """ if key not in self.cache: return -1
Пройди собеседование в топ-компанию
Платформа для подготовки

Решай алгоритмические задачи как профи

✓ Популярные алгоритмы✓ Разбор решений✓ AI помощь
Начать сейчас
Программист за работой
    # Перемещаем элемент в конец (как недавно использованный)
    value = self.cache.pop(key)
    self.cache[key] = value
    return value

def put(self, key, value):
    """
    Добавление или обновление значения в кэше.
    Если кэш полон, удаляем наименее недавно использованный элемент.
    
    Args:
        key: Ключ для добавления или обновления
        value: Значение для сохранения
    """
    # Если ключ уже существует, удаляем его (чтобы обновить позицию)
    if key in self.cache:
        self.cache.pop(key)
    # Если кэш достиг максимального размера, удаляем первый элемент (LRU)
    elif len(self.cache) >= self.capacity:
        self.cache.popitem(last=False)
    
    # Добавляем новый элемент в конец
    self.cache[key] = value

Пример использования

cache = LRUCache(2) cache.put(1, 1) cache.put(2, 2) print(cache.get(1)) # Вывод: 1 cache.put(3, 3) # Вытесняет ключ 2 print(cache.get(2)) # Вывод: -1 (не найден)


### 3. ООП и принципы проектирования в Python

#### Типичные вопросы:

| Вопрос | На что обратить внимание |
|--------|--------------------------|
| Как работает наследование в Python? | Множественное наследование, MRO |
| Что такое магические методы? | `__init__`, `__str__`, операторы, контекстные менеджеры |
| Принципы SOLID в Python | Реализация через классы и функции |
| Что такое миксины? | Примеры использования, преимущества |
| Отличие `__new__` от `__init__`? | Создание объекта vs инициализация |

#### Пример задания: Реализовать класс с кастомным контекстным менеджером

```python
import time

class Timer:
    """
    Контекстный менеджер для измерения времени выполнения блока кода.
    
    Attributes:
        name: Название таймера для вывода
        elapsed: Время выполнения блока кода в секундах
    """
    def __init__(self, name="Timer"):
        self.name = name
        self.elapsed = 0
    
    def __enter__(self):
        """Вызывается при входе в блок with"""
        self.start_time = time.time()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        """
        Вызывается при выходе из блока with.
        
        Args:
            exc_type: Тип исключения, если оно возникло
            exc_val: Значение исключения
            exc_tb: Трассировка исключения
        """
        self.elapsed = time.time() - self.start_time
        print(f"{self.name} выполнился за {self.elapsed:.6f} секунд")
        
        # Возвращаем False, чтобы исключения прокидывались дальше
        return False
    
    def __str__(self):
        return f"{self.name}: {self.elapsed:.6f} сек"

# Пример использования
def slow_operation():
    # Имитация долгой операции
    time.sleep(1.5)

with Timer("Медленная операция") as timer:
    slow_operation()
    
print(f"Можно использовать результат: {timer}")

4. Функциональное программирование в Python

Типичные вопросы:

ВопросНа что обратить внимание
Как работают lambda-функции?Анонимные функции, ограничения
Что такое функции высшего порядка?map, filter, reduce, примеры
Что такое декораторы и как их реализовать?Функциональность, параметры декораторов
Что такое замыкания?Область видимости, nonlocal
Функциональные возможности в functools и itertoolspartial, lru_cache, chain, combinations

Пример задания: Написать декоратор для мемоизации функции

import functools from typing import Any, Callable, Dict, Hashable, Tuple def memoize(func: Callable) -> Callable: """ Декоратор для мемоизации результатов функции. Сохраняет результаты в словаре для повторного использования. Args: func: Функция для мемоизации Returns: Обёрнутая функция с мемоизацией """ cache: Dict[Tuple[Hashable, ...], Any] = {} @functools.wraps(func) def wrapper(*args, **kwargs): # Создаём ключ для кэша из аргументов # Для kwargs сортируем по ключам, чтобы порядок не влиял key = ( args, tuple(sorted((k, v) for k, v in kwargs.items())) ) # Если результат есть в кэше, возвращаем его if key in cache: return cache[key] # Иначе вычисляем и сохраняем result = func(*args, **kwargs) cache[key] = result return result # Добавляем доступ к кэшу для тестирования/отладки wrapper.cache = cache return wrapper # Пример использования @memoize def fibonacci(n: int) -> int: """Вычисляет n-е число Фибоначчи (рекурсивно)""" if n <= 1: return n return fibonacci(n-1) + fibonacci(n-2) # Тестирование print(fibonacci(35)) # Быстро выполняется из-за мемоизации print(len(fibonacci.cache)) # Показывает количество сохранённых результатов

5. Асинхронное программирование

Типичные вопросы:

ВопросНа что обратить внимание
Как работает asyncio?Event loop, корутины, задачи
Что такое coroutine в Python?async/await, асинхронные генераторы
Отличие многопоточности от асинхронностиGIL, параллелизм vs конкурентность
Как работать с асинхронным вводом-выводом?aiohttp, asyncpg, сравнение с синхронными аналогами
Когда использовать asyncio vs threading vs multiprocessing?Сценарии применения, преимущества

Пример задания: Реализовать асинхронный веб-скрапер

import asyncio import aiohttp from bs4 import BeautifulSoup from typing import List, Dict, Set, Any class AsyncWebScraper: """ Асинхронный веб-скрапер, который может обрабатывать несколько URL параллельно. Attributes: max_concurrency: Максимальное количество параллельных запросов timeout: Таймаут запроса в секундах seen_urls: Множество уже обработанных URL для избежания дубликатов """ def __init__(self, max_concurrency: int = 10, timeout: int = 30): self.max_concurrency = max_concurrency self.timeout = timeout self.seen_urls: Set[str] = set() async def fetch_url(self, session: aiohttp.ClientSession, url: str) -> Dict[str, Any]: """ Асинхронно загружает и парсит веб-страницу. Args: session: Сессия aiohttp для выполнения запросов url: URL для загрузки Returns: Словарь с URL, заголовком и списком ссылок со страницы """ if url in self.seen_urls: return {"url": url, "error": "Already processed"} self.seen_urls.add(url) try: async with session.get(url, timeout=self.timeout) as response: if response.status != 200: return { "url": url, "status": response.status, "error": "Invalid status code" } html = await response.text() soup = BeautifulSoup(html, 'html.parser') # Получаем заголовок страницы title = soup.title.text if soup.title else "No title" # Получаем все ссылки links = [] for link in soup.find_all('a', href=True): href = link['href'] if href.startswith('http'): links.append(href) return { "url": url, "title": title, "links": links, "status": response.status } except asyncio.TimeoutError: return {"url": url, "error": "Timeout"} except Exception as e: return {"url": url, "error": str(e)} async def scrape_urls(self, urls: List[str]) -> List[Dict[str, Any]]: """ Асинхронно скрапит список URL с ограничением параллельных запросов. Args: urls: Список URL для обработки Returns: Список результатов скрапинга для каждого URL """ results = [] # Используем семафор для ограничения параллельных запросов semaphore = asyncio.Semaphore(self.max_concurrency) async def bounded_fetch(session, url): async with semaphore: return await self.fetch_url(session, url) # Используем контекстный менеджер для создания и закрытия сессии async with aiohttp.ClientSession() as session: # Создаем список задач tasks = [bounded_fetch(session, url) for url in urls] # Ждем завершения всех задач results = await asyncio.gather(*tasks) return results # Пример использования async def main(): urls = [ "https://python.org", "https://github.com", "https://stackoverflow.com", # добавьте больше URL по необходимости ] scraper = AsyncWebScraper(max_concurrency=5) results = await scraper.scrape_urls(urls) for result in results: if "error" in result: print(f"Error scraping {result['url']}: {result['error']}") else: print(f"Scraped {result['url']} - Title: {result['title']}") print(f"Found {len(result['links'])} links") # Запуск асинхронной функции if __name__ == "__main__": asyncio.run(main())

6. Фреймворки и библиотеки: Django, Flask, FastAPI

Типичные вопросы:

ВопросНа что обратить внимание
Как работает ORM в Django?Models, QuerySet, миграции
Что такое middleware?Примеры в Django/Flask, создание своих
Сравнение Django с FlaskПлюсы/минусы, сценарии использования
Как обеспечить безопасность веб-приложения?CSRF, XSS, SQL-инъекции
Что такое Dependency Injection в FastAPI?Принцип работы, преимущества

Пример задания: Создать REST API с использованием FastAPI

from fastapi import FastAPI, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from pydantic import BaseModel from typing import List, Optional import jwt from datetime import datetime, timedelta import hashlib app = FastAPI(title="Todo API") # --- Модели данных --- class TodoBase(BaseModel): title: str description: Optional[str] = None is_completed: bool = False class TodoCreate(TodoBase): pass class Todo(TodoBase): id: int owner_id: int class Config: orm_mode = True class UserBase(BaseModel): username: str class UserCreate(UserBase): password: str class User(UserBase): id: int class Config: orm_mode = True # --- Эмуляция БД --- fake_users_db = {} fake_todos_db = {} todo_counter = 0 # --- Аутентификация --- SECRET_KEY = "very_secret_key_replace_in_production" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 30 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") def hash_password(password: str) -> str: return hashlib.sha256(password.encode()).hexdigest() def create_access_token(data: dict, expires_delta: timedelta = None): to_encode = data.copy() expire = datetime.utcnow() + ( expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) ) to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) async def get_current_user(token: str = Depends(oauth2_scheme)): try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username = payload.get("sub") if username is None or username not in fake_users_db: raise HTTPException(status_code=401, detail="Invalid credentials") except jwt.PyJWTError: raise HTTPException(status_code=401, detail="Invalid credentials") return fake_users_db[username] # --- Эндпоинты --- @app.post("/users/", response_model=User, status_code=status.HTTP_201_CREATED) async def create_user(user: UserCreate): if user.username in fake_users_db: raise HTTPException(status_code=400, detail="Username already registered") user_id = len(fake_users_db) + 1 fake_users_db[user.username] = { "id": user_id, "username": user.username, "password_hash": hash_password(user.password) } return {"id": user_id, "username": user.username} @app.post("/token") async def login(form_data: OAuth2PasswordRequestForm = Depends()): username = form_data.username if (username not in fake_users_db or hash_password(form_data.password) != fake_users_db[username]["password_hash"]): raise HTTPException(status_code=400, detail="Incorrect username or password") access_token = create_access_token(data={"sub": username}) return {"access_token": access_token, "token_type": "bearer"} @app.post("/todos/", response_model=Todo, status_code=status.HTTP_201_CREATED) async def create_todo(todo: TodoCreate, current_user = Depends(get_current_user)): global todo_counter todo_counter += 1 todo_dict = todo.dict() todo_dict.update({ "id": todo_counter, "owner_id": current_user["id"] }) fake_todos_db[todo_counter] = todo_dict return todo_dict @app.get("/todos/", response_model=List[Todo]) async def read_todos(current_user = Depends(get_current_user)): return [ todo for todo in fake_todos_db.values() if todo["owner_id"] == current_user["id"] ] @app.get("/todos/{todo_id}", response_model=Todo) async def read_todo(todo_id: int, current_user = Depends(get_current_user)): if (todo_id not in fake_todos_db or fake_todos_db[todo_id]["owner_id"] != current_user["id"]): raise HTTPException(status_code=404, detail="Todo not found") return fake_todos_db[todo_id] @app.put("/todos/{todo_id}", response_model=Todo) async def update_todo(todo_id: int, todo: TodoCreate, current_user = Depends(get_current_user)): if (todo_id not in fake_todos_db or fake_todos_db[todo_id]["owner_id"] != current_user["id"]): raise HTTPException(status_code=404, detail="Todo not found") todo_dict = todo.dict() todo_dict.update({ "id": todo_id, "owner_id": current_user["id"] }) fake_todos_db[todo_id] = todo_dict return todo_dict @app.delete("/todos/{todo_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_todo(todo_id: int, current_user = Depends(get_current_user)): if (todo_id not in fake_todos_db or fake_todos_db[todo_id]["owner_id"] != current_user["id"]): raise HTTPException(status_code=404, detail="Todo not found") del fake_todos_db[todo_id] return None # Запуск с uvicorn main:app --reload

7. Базы данных и ORM

Типичные вопросы:

ВопросНа что обратить внимание
Как работать с SQL в Python?SQLAlchemy, psycopg2, sqlite3
Нормализация баз данныхФормы нормализации, внешние ключи
Оптимизация запросовИндексы, explain plan, n+1 проблема
NoSQL базы данных с PythonMongoDB, Redis, примеры использования
Миграции баз данныхAlembic, Django migrations

Пример задания: Реализовать модели для блога с использованием SQLAlchemy

from sqlalchemy import ( Column, Integer, String, Text, Boolean, DateTime, ForeignKey, create_engine, func ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relationship, sessionmaker from datetime import datetime from typing import List, Optional # Создаем базовый класс для моделей Base = declarative_base() class User(Base): """Модель пользователя.""" __tablename__ = "users" id = Column(Integer, primary_key=True) username = Column(String(50), unique=True, nullable=False) email = Column(String(100), unique=True, nullable=False) password_hash = Column(String(128), nullable=False) is_active = Column(Boolean, default=True) created_at = Column(DateTime, default=datetime.utcnow) # Отношения posts = relationship("Post", back_populates="author", cascade="all, delete-orphan") comments = relationship("Comment", back_populates="author", cascade="all, delete-orphan") def __repr__(self) -> str: return f"<User {self.username}>" class Category(Base): """Модель категории блога.""" __tablename__ = "categories" id = Column(Integer, primary_key=True) name = Column(String(50), unique=True, nullable=False) slug = Column(String(50), unique=True, nullable=False) # Отношения posts = relationship("Post", back_populates="category") def __repr__(self) -> str: return f"<Category {self.name}>" class Post(Base): """Модель поста в блоге.""" __tablename__ = "posts" id = Column(Integer, primary_key=True) title = Column(String(100), nullable=False) slug = Column(String(100), unique=True, nullable=False) content = Column(Text, nullable=False) published = Column(Boolean, default=False) created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) # Внешние ключи author_id = Column(Integer, ForeignKey("users.id"), nullable=False) category_id = Column(Integer, ForeignKey("categories.id"), nullable=False) # Отношения author = relationship("User", back_populates="posts") category = relationship("Category", back_populates="posts") comments = relationship("Comment", back_populates="post", cascade="all, delete-orphan") tags = relationship("Tag", secondary="post_tags", back_populates="posts") def __repr__(self) -> str: return f"<Post {self.title}>" class Comment(Base): """Модель комментария к посту.""" __tablename__ = "comments" id = Column(Integer, primary_key=True) content = Column(Text, nullable=False) created_at = Column(DateTime, default=datetime.utcnow) # Внешние ключи post_id = Column(Integer, ForeignKey("posts.id"), nullable=False) author_id = Column(Integer, ForeignKey("users.id"), nullable=False) parent_id = Column(Integer, ForeignKey("comments.id"), nullable=True) # Отношения post = relationship("Post", back_populates="comments") author = relationship("User", back_populates="comments") replies = relationship("Comment", backref=backref("parent", remote_side=[id]), cascade="all, delete-orphan") def __repr__(self) -> str: return f"<Comment {self.id} by {self.author_id}>" class Tag(Base): """Модель тега для постов.""" __tablename__ = "tags" id = Column(Integer, primary_key=True) name = Column(String(30), unique=True, nullable=False) # Отношения posts = relationship("Post", secondary="post_tags", back_populates="tags") def __repr__(self) -> str: return f"<Tag {self.name}>" # Таблица связи для многие-ко-многим между постами и тегами post_tags = Table( "post_tags", Base.metadata, Column("post_id", Integer, ForeignKey("posts.id"), primary_key=True), Column("tag_id", Integer, ForeignKey("tags.id"), primary_key=True) ) # Пример использования моделей def create_sample_data(): # Создаем соединение с БД engine = create_engine('sqlite:///blog.db') Base.metadata.create_all(engine) # Создаем сессию Session = sessionmaker(bind=engine) session = Session() try: # Пользователи user1 = User( username="john_doe", email="john@example.com", password_hash="hashed_password" ) session.add(user1) # Категории category1 = Category(name="Python", slug="python") session.add(category1) # Теги tag1 = Tag(name="tutorial") tag2 = Tag(name="programming") session.add_all([tag1, tag2]) # Посты post1 = Post( title="Getting Started with Python", slug="getting-started-with-python", content="This is a beginner's guide to Python programming...", author=user1, category=category1, published=True ) post1.tags.extend([tag1, tag2]) session.add(post1) # Комментарии comment1 = Comment( content="Great tutorial!", author=user1, post=post1 ) session.add(comment1) # Фиксируем изменения session.commit() print("Sample data created successfully!") except Exception as e: session.rollback() print(f"Error creating sample data: {e}") finally: session.close() if __name__ == "__main__": create_sample_data()

8. Алгоритмы и структуры данных

Типичные вопросы:

ВопросНа что обратить внимание
Сложность алгоритмовBig O нотация, оценка времени и памяти
Алгоритмы сортировкиБыстрая сортировка, сортировка слиянием
Алгоритмы поискаБинарный поиск, поиск в глубину/ширину
Базовые структуры данныхСписки, стеки, очереди, деревья, графы
Оптимизация рекурсивных алгоритмовМемоизация, хвостовая рекурсия

Пример задания: Реализовать поиск кратчайшего пути в графе с помощью алгоритма Дейкстры

import heapq from typing import Dict, List, Tuple, Any def dijkstra( graph: Dict[Any, List[Tuple[Any, int]]], start: Any ) -> Dict[Any, Tuple[int, List[Any]]]: """ Реализация алгоритма Дейкстры для поиска кратчайших путей. Args: graph: Словарь смежности, где ключи - вершины, значения - список кортежей (соседняя вершина, вес) start: Начальная вершина Returns: Словарь, где ключи - вершины, значения - кортежи (дистанция, путь) """ # Инициализация distances = {vertex: float('infinity') for vertex in graph} distances[start] = 0 # Для каждой вершины будем хранить путь к ней paths = {vertex: [] for vertex in graph} paths[start] = [start] # Приоритетная очередь для выбора следующей вершины # (расстояние, вершина) priority_queue = [(0, start)] while priority_queue: # Извлекаем вершину с наименьшим расстоянием current_distance, current_vertex = heapq.heappop(priority_queue) # Если текущее расстояние больше, чем известное, пропускаем if current_distance > distances[current_vertex]: continue # Проверяем всех соседей текущей вершины for neighbor, weight in graph[current_vertex]: distance = current_distance + weight # Если найден более короткий путь if distance < distances[neighbor]: # Обновляем расстояние distances[neighbor] = distance # Обновляем путь paths[neighbor] = paths[current_vertex] + [neighbor] # Добавляем в очередь для дальнейшей обработки heapq.heappush(priority_queue, (distance, neighbor)) # Возвращаем словарь с расстояниями и путями return {vertex: (distances[vertex], paths[vertex]) for vertex in graph} # Пример использования def example_usage(): # Представление графа в виде списка смежности с весами # A --- 1 --- B # | | # 4 2 # | | # C --- 3 --- D graph = { 'A': [('B', 1), ('C', 4)], 'B': [('A', 1), ('D', 2)], 'C': [('A', 4), ('D', 3)], 'D': [('B', 2), ('C', 3)] } # Находим кратчайшие пути от вершины A result = dijkstra(graph, 'A') # Выводим результаты for vertex, (distance, path) in result.items(): print(f"Shortest path to {vertex}: {' -> '.join(path)} with distance {distance}") if __name__ == "__main__": example_usage()

9. Тестирование и отладка

Типичные вопросы:

ВопросНа что обратить внимание
Какие типы тестирования вы знаете?Unit, integration, E2E, их отличия
Как писать тесты в Python?pytest, unittest, mock-объекты
Что такое TDD?Test-Driven Development, преимущества
Как отлаживать Python-код?pdb, logging, профилирование
Как проводить мониторинг приложений?Prometheus, Grafana, логирование

Пример задания: Написать тесты для функции валидации электронной почты

import re import pytest from typing import Union def validate_email(email: str) -> Union[bool, str]: """ Проверяет, является ли строка допустимым адресом электронной почты. Args: email: Строка для проверки Returns: True если почта валидна, иначе строка с описанием ошибки """ if not isinstance(email, str): return "Email must be a string" if not email: return "Email cannot be empty" # Проверка длины if len(email) > 254: return "Email is too long" # Базовая проверка с помощью регулярного выражения pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,} if not re.match(pattern, email): return "Invalid email format" # Дополнительные проверки local_part, domain = email.split('@', 1) # Проверка локальной части if len(local_part) > 64: return "Local part is too long" # Проверка домена if domain.startswith('.') or domain.endswith('.'): return "Domain cannot start or end with a dot" if '..' in domain: return "Domain cannot contain consecutive dots" # Все проверки прошли успешно return True # Тесты с использованием pytest def test_valid_emails(): """Тестирование валидных адресов электронной почты.""" valid_emails = [ "test@example.com", "john.doe@example.co.uk", "john.doe+tag@example.org", "john_doe@example.com", "j@example.com" ] for email in valid_emails: assert validate_email(email) is True, f"Email {email} should be valid" def test_invalid_emails(): """Тестирование невалидных адресов электронной почты.""" invalid_emails = [ # Неверный формат "test", "test@", "@example.com", "test@.com", "test@example.", "test@exam ple.com", # Слишком длинные части "a" * 65 + "@example.com", "test@" + "a" * 255 + ".com", # Некорректные символы "test!@example.com", "test@example..com", ] for email in invalid_emails: result = validate_email(email) assert isinstance(result, str), f"Email {email} should be invalid" def test_edge_cases(): """Тестирование граничных случаев.""" # Некорректные типы assert isinstance(validate_email(None), str) assert isinstance(validate_email(123), str) # Пустая строка assert isinstance(validate_email(""), str) # Граничные случаи с длиной long_local = "a" * 64 assert validate_email(f"{long_local}@example.com") is True long_domain = "a" * 63 assert validate_email(f"test@{long_domain}.com") is True # Запуск тестов через pytest if __name__ == "__main__": # pytest.main(['-v']) # Для ручного тестирования print(validate_email("test@example.com")) # True print(validate_email("invalid@")) # Сообщение об ошибке

10. DevOps и инфраструктура

Типичные вопросы:

ВопросНа что обратить внимание
Как упаковать Python-приложение?Pip, Poetry, virtualenv, Docker
CI/CD для Python-проектовGitHub Actions, Jenkins, тестирование
Как развертывать Python-приложения?WSGI, ASGI, Gunicorn, Docker, Kubernetes
Мониторинг производительностиИнструменты профилирования, логирование
Оптимизация производительностиКэширование, масштабирование, узкие места

Пример задания: Создать Docker-контейнер для Python Flask-приложения

# Используем официальный образ Python FROM python:3.9-slim # Устанавливаем рабочую директорию WORKDIR /app # Копируем файлы зависимостей COPY requirements.txt . # Устанавливаем зависимости RUN pip install --no-cache-dir -r requirements.txt # Копируем код приложения COPY . . # Открываем порт, на котором будет работать приложение EXPOSE 5000 # Устанавливаем переменные окружения ENV FLASK_APP=app.py ENV FLASK_RUN_HOST=0.0.0.0 ENV FLASK_ENV=production # Запускаем Gunicorn для продакшена CMD ["gunicorn", "--bind", "0.0.0.0:5000", "app:app"]

requirements.txt

flask==2.0.1
gunicorn==20.1.0

app.py

import os from flask import Flask, jsonify app = Flask(__name__) @app.route('/') def home(): return jsonify({ 'message': 'Hello, World!', 'environment': os.environ.get('FLASK_ENV', 'development') }) @app.route('/health') def health(): return jsonify({ 'status': 'healthy' }) if __name__ == '__main__': app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 5000)))

docker-compose.yml

version: '3' services: web: build: . ports: - "5000:5000" environment: - FLASK_ENV=production restart: always healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000/health"] interval: 30s timeout: 10s retries: 3 start_period: 5s

Рекомендации по подготовке к собеседованию 💡

Теоретическая подготовка:

ОбластьКлючевые концепцииРесурсы
Python CoreТипы данных, коллекции, ООП, генераторыОфициальная документация, "Fluent Python"
АлгоритмыСложность, сортировка, поиск, структуры данных"Grokking Algorithms", LeetCode
Веб-фреймворкиDjango, Flask, FastAPI, REST APIДокументация фреймворков, Real Python
Базы данныхSQL, ORM, NoSQL, индексыДокументация SQLAlchemy, Postgres
Асинхронностьasyncio, корутины, многопоточностьДокументация asyncio
DevOpsDocker, CI/CD, развертываниеDocker Docs, GitHub Actions

Практические советы:

  1. Создайте проекты для портфолио:

    • Pet-проекты на Django или Flask
    • Утилиты для работы с данными
    • Библиотека с хорошим README и документацией
  2. Практикуйте алгоритмические задачи:

    • Прорешивайте задачи вслух, объясняя свой подход
  3. Улучшайте навыки работы с кодом:

    • Code review на GitHub
    • Учитесь писать чистый, PEP8-совместимый код
    • Изучайте open-source Python проекты
  4. Подготовьте вопросы для интервьюера:

    • О технологическом стеке команды
    • О процессах разработки
    • О карьерных возможностях

Типичные ошибки на собеседованиях

  1. 🤦‍♂️ Незнание основ языка (разница mutable/immutable, работа с списками)
  2. 😅 Непонимание областей видимости и механизма LEGB (Local, Enclosing, Global, Built-in)
  3. 🔄 Сложности с объяснением разницы между lambda, map, filter и list comprehensions
  4. 🧮 Недостаточное понимание производительности различных структур данных
  5. 🧠 Поверхностное знание фреймворков без понимания их внутренних механизмов

Шпаргалка: Основные концепции для быстрого повторения

Python основы:

# 1. List comprehensions vs map/filter numbers = [1, 2, 3, 4, 5] # List comprehension squares_comp = [x**2 for x in numbers if x % 2 == 0] # Эквивалент с map и filter squares_map = list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, numbers))) print(squares_comp) # [4, 16] print(squares_map) # [4, 16] # 2. Генераторы def fibonacci_gen(n): a, b = 0, 1 for _ in range(n): yield a a, b = b, a + b # Использование генератора - эффективно по памяти for num in fibonacci_gen(10): print(num, end=' ') # 0 1 1 2 3 5 8 13 21 34 # 3. Контекстные менеджеры class DatabaseConnection: def __enter__(self): print("Opening connection") return self def __exit__(self, exc_type, exc_val, exc_tb): print("Closing connection") # Возвращаем False, чтобы не подавлять исключения return False def query(self, sql): print(f"Executing: {sql}") return ["result1", "result2"] # Использование with DatabaseConnection() as conn: results = conn.query("SELECT * FROM users") print(results) # 4. Декораторы import functools import time def timing_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(f"{func.__name__} took {end_time - start_time:.2f} seconds") return result return wrapper @timing_decorator def slow_function(delay): time.sleep(delay) return "Done" print(slow_function(1)) # slow_function took 1.00 seconds \n Done

Django основы:

# models.py from django.db import models from django.contrib.auth.models import User class Category(models.Model): name = models.CharField(max_length=100) slug = models.SlugField(unique=True) class Meta: verbose_name_plural = "Categories" def __str__(self): return self.name class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) published = models.BooleanField(default=False) author = models.ForeignKey(User, on_delete=models.CASCADE) category = models.ForeignKey(Category, on_delete=models.CASCADE) def __str__(self): return self.title def get_summary(self): return f"{self.content[:150]}..." # views.py from django.shortcuts import render, get_object_or_404 from django.views.generic import ListView, DetailView from .models import Article, Category class ArticleListView(ListView): model = Article context_object_name = 'articles' template_name = 'blog/article_list.html' paginate_by = 10 def get_queryset(self): # Оптимизация запроса return Article.objects.filter( published=True ).select_related( 'author', 'category' ).order_by('-created_at') class ArticleDetailView(DetailView): model = Article context_object_name = 'article' template_name = 'blog/article_detail.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['related_articles'] = Article.objects.filter( category=self.object.category ).exclude( id=self.object.id )[:5] return context

FastAPI основы:

from fastapi import FastAPI, Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from pydantic import BaseModel from typing import List, Optional import jwt app = FastAPI() # Модели данных class ItemBase(BaseModel): title: str description: Optional[str] = None class ItemCreate(ItemBase): pass class Item(ItemBase): id: int owner_id: int class Config: orm_mode = True # Зависимости oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") async def get_current_user(token: str = Depends(oauth2_scheme)): credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) try: payload = jwt.decode(token, "secret_key", algorithms=["HS256"]) user_id: int = payload.get("sub") if user_id is None: raise credentials_exception except jwt.PyJWTError: raise credentials_exception # В реальном приложении здесь будет запрос к БД return {"id": user_id, "username": "example_user"} # Роуты @app.get("/items/", response_model=List[Item]) async def read_items(current_user = Depends(get_current_user)): # В реальном приложении здесь будет запрос к БД return [ {"id": 1, "title": "Item 1", "description": "Description 1", "owner_id": current_user["id"]}, {"id": 2, "title": "Item 2", "description": "Description 2", "owner_id": current_user["id"]} ] @app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED) async def create_item(item: ItemCreate, current_user = Depends(get_current_user)): # В реальном приложении здесь будет запись в БД new_item = { "id": 3, # В реальном приложении ID будет генерироваться БД **item.dict(), "owner_id": current_user["id"] } return new_item

Заключение

Собеседование на позицию Python-разработчика представляет собой:

  • 🧠 Комплексную проверку как теоретических знаний языка, так и практических навыков
  • 🔍 Оценку понимания экосистемы Python, включая фреймворки и библиотеки
  • 🏗️ Проверку способности решать алгоритмические задачи и проектировать системы
  • 🌟 Возможность продемонстрировать свой потенциал и опыт
  • 💼 Шанс оценить соответствие команды и компании вашим карьерным целям

Успешное прохождение технических собеседований по Python требует не только глубокого понимания языка и связанных технологий, но и умения ясно коммуницировать свои мысли, анализировать задачи и предлагать эффективные решения.

Помните, что собеседование — это двусторонний процесс. Вы не только демонстрируете свои навыки потенциальному работодателю, но и определяете, подходит ли вам эта компания и команда. Задавайте вопросы, интересуйтесь процессами и культурой — это поможет вам принять правильное решение о своей карьере.

Дополнительные ресурсы для подготовки

Книги и документация:

  • "Fluent Python" - Лучано Рамальо
  • "Python Cookbook" - Дэвид Бизли
  • "Clean Code in Python" - Марианно Ансельми
  • Официальная документация Python, Django, Flask, FastAPI

Онлайн-платформы:

  • Real Python – туториалы и статьи
  • Python.org – официальные ресурсы

Сообщества и форумы:

  • Stack Overflow
  • Python сабреддиты (r/python, r/learnpython)
  • GitHub и открытые исходники Python-проектов
  • PyConferences – записи выступлений с конференций

Постоянная практика, работа над реальными проектами, участие в open-source и регулярное изучение новых возможностей Python-экосистемы — лучшие способы не только подготовиться к собеседованиям, но и стать высококлассным Python-разработчиком, востребованным на рынке труда.