100+ вопросов на собеседовании Python разработчика

Python — один из самых востребованных языков в 2026 году. Веб-разработка, data science, автоматизация, ML — везде нужны питонисты. Но и конкуренция соответствующая.
В этой статье — 100+ реальных вопросов с собеседований в Яндекс, Тинькофф, Сбер и других компаниях. Разбиты по уровням и темам, с ответами и примерами кода.
Как пользоваться этим гайдом
Junior (0-1 год): фокус на разделах 1-4 Middle (1-3 года): всё, кроме раздела 8 Senior (3+ лет): ��собое внимание на разделы 5-8
Время на подготовку: Junior — 2-3 недели, Middle — 3-4 недели, Senior — 1-2 недели на повторение.
1. Основы Python
Базовые вопросы
В: Какие типы данных есть в Python?
Изменяемые (mutable): list, dict, set, bytearray Неизменяемые (immutable): int, float, str, tuple, frozenset, bytes
В: Чем отличается list от tuple?
List — изменяемый, tuple — нет. Tuple быстрее, занимает меньше памяти, может быть ключом словаря. Используйте tuple для данных, которые не должны меняться.
# tuple как ключ словаря
coordinates = {(55.75, 37.62): "Moscow", (59.93, 30.31): "SPb"}
В: Что такое list comprehension? Когда использовать?
Компактный способ создания списков. Читабельнее циклов для простых трансформаций.
# Вместо
squares = []
for x in range(10):
squares.append(x ** 2)
# Используем
squares = [x ** 2 for x in range(10)]
# С условием
even_squares = [x ** 2 for x in range(10) if x % 2 == 0]
В: Разница между is и ==?
== сравнивает значения, is — идентичность объектов (один и тот же объект в памяти).
a = [1, 2, 3]
b = [1, 2, 3]
c = a
a == b # True (одинаковые значения)
a is b # False (разные объекты)
a is c # True (один объект)
В: Что выведет этот код?
def func(a, lst=[]):
lst.append(a)
return lst
print(func(1)) # [1]
print(func(2)) # [1, 2] — не [2]!
Изменяемые аргументы по умолчанию — частая ловушка. Список создаётся один раз при определении функции.
# Правильно:
def func(a, lst=None):
if lst is None:
lst = []
lst.append(a)
return lst
Строки и форматирование
В: Способы форматирования строк в Python?
name, age = "Alice", 30
# %-форматирование (устаревший)
"Name: %s, Age: %d" % (name, age)
# str.format()
"Name: {}, Age: {}".format(name, age)
# f-strings (рекомендуется, Python 3.6+)
f"Name: {name}, Age: {age}"
# Template strings (для пользовательского ввода)
from string import Template
Template("Name: $name").substitute(name=name)
В: Строки в Python — mutable или immutable?
Immutable. Каждая операция создаёт новую строку.
s = "hello"
s[0] = "H" # TypeError!
# Правильно:
s = "H" + s[1:]
2. Структуры данных
Словари и множества
В: Как работает dict внутри?
Хеш-таблица. Ключ хешируется, хеш определяет позицию в массиве. При коллизиях используется open addressing.
Сложность операций: get/set/delete — O(1) в среднем, O(n) в худшем случае при множестве коллизий.
В: Почему ключи dict должны быть hashable?
Потому что dict использует хеш для поиска. Hashable объекты: их хеш не меняется за время жизни. Все immutable типы hashable.
# Можно
d = {(1, 2): "tuple", "str": "string", 42: "int"}
# Нельзя
d = {[1, 2]: "list"} # TypeError: unhashable type: 'list'
В: Разница между dict и OrderedDict?
С Python 3.7+ обычный dict сохраняет порядок вставки. OrderedDict нужен для:
- Сравнения с учётом порядка
- Метод move_to_end()
- Совместимость с Python < 3.7
В: Как получить ключ с максимальным значением?
d = {"a": 10, "b": 5, "c": 20}
# Способ 1
max(d, key=d.get) # "c"
# Способ 2
max(d, key=lambda k: d[k])
# Способ 3 (если нужны и ключ, и значение)
max(d.items(), key=lambda x: x[1]) # ("c", 20)
Продвинутые структуры
В: Что такое defaultdict?
Словарь с значением по умолчанию для несуществующих ключей.
from collections import defaultdict
# Подсчёт слов
word_count = defaultdict(int)
for word in words:
word_count[word] += 1 # Не нужен if/get
# Группировка
groups = defaultdict(list)
for item in items:
groups[item.category].append(item)
В: Когда использовать deque вместо list?
Когда нужны быстрые операции с обоих концов.
from collections import deque
# list: append O(1), pop O(1), insert(0) O(n), pop(0) O(n)
# deque: append O(1), pop O(1), appendleft O(1), popleft O(1)
queue = deque(maxlen=100) # автоматически удаляет старые элементы
В: Что такое Counter?
from collections import Counter
c = Counter("abracadabra")
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
c.most_common(2) # [('a', 5), ('b', 2)]
c['a'] # 5
c['z'] # 0 (не KeyError!)
3. Функции и ООП
Функции
**В: *args и kwargs — что это?
def func(*args, **kwargs):
print(args) # tuple позиционных аргументов
print(kwargs) # dict именованных аргументов
func(1, 2, 3, name="Alice", age=30)
# args = (1, 2, 3)
# kwargs = {"name": "Alice", "age": 30}
В: Что такое декоратор? Напишите пример.
Функция, которая принимает функцию и возвращает функцию.
import functools
import time
def timer(func):
@functools.wraps(func) # сохраняет метаданные
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time() - start:.2f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
В: Декоратор с параметрами?
def repeat(times):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
В: Что такое замыкание (closure)?
Функция, которая запоминает переменные из внешней области видимости.
def make_multiplier(n):
def multiplier(x):
return x * n # n "замкнута" внутри
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
double(5) # 10
triple(5) # 15
ООП
В: Разница между @classmethod и @staticmethod?
class MyClass:
class_var = "I'm a class variable"
def instance_method(self):
# Доступ к instance и class
return self.class_var
@classmethod
def class_method(cls):
# Доступ только к class, не к instance
return cls.class_var
@staticmethod
def static_method():
# Нет доступа ни к instance, ни к class
# По сути обычная функция внутри класса
return "I'm just a function"
В: Что такое MRO (Method Resolution Order)?
Порядок поиска методов при множественном наследовании. Python использует алгоритм 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)
В: Что такое дескриптор?
Объект с методами __get__, __set__, __delete__. Управляет доступом к атрибутам.
class Positive:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, type=None):
return obj.__dict__.get(self.name, 0)
def __set__(self, obj, value):
if value < 0:
raise ValueError("Must be positive")
obj.__dict__[self.name] = value
class Account:
balance = Positive()
acc = Account()
acc.balance = 100 # OK
acc.balance = -50 # ValueError
В: Разница между str и repr?
__str__ — для пользователей, __repr__ — для разработчиков (должен быть однозначным).
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self):
return f"Point({self.x}, {self.y})" # можно скопировать и выполнить
def __str__(self):
return f"({self.x}, {self.y})" # для вывода пользователю
В: Что такое slots?
Оптимизация памяти. Вместо dict используется фиксированный набор атрибутов.
class WithSlots:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x, self.y = x, y
class WithoutSlots:
def __init__(self, x, y):
self.x, self.y = x, y
# WithSlots использует ~40% меньше памяти
# Нельзя добавлять новые атрибуты
4. Итераторы и генераторы
В: Разница между итератором и итерируемым?
Итерируемый (iterable) — объект с методом __iter__() (list, str, dict).
Итератор (iterator) — объект с методами __iter__() и __next__().
lst = [1, 2, 3] # iterable
iterator = iter(lst) # iterator
next(iterator) # 1
next(iterator) # 2
В: Что такое генератор? Зачем нужен?
Функция с yield вместо return. Ленивое вычисление — экономит память.
# Список — всё в памяти сразу
def get_squares_list(n):
return [x ** 2 for x in range(n)]
# Генератор — по одному элементу
def get_squares_gen(n):
for x in range(n):
yield x ** 2
# Для n = 1_000_000:
# Список: ~40 MB памяти
# Генератор: ~100 bytes
В: Generator expression vs list comprehension?
# List comprehension — создаёт список сразу
squares_list = [x ** 2 for x in range(1000000)]
# Generator expression — ленивый
squares_gen = (x ** 2 for x in range(1000000))
# Используйте генератор, когда:
# - Большой объём данных
# - Нужен только один проход
# - Возможно, не понадобятся все элементы
В: Как работает yield from?
Делегирует генерацию другому итерируемому объекту.
def flatten(nested):
for item in nested:
if isinstance(item, list):
yield from flatten(item) # рекурсивная делегация
else:
yield item
list(flatten([1, [2, [3, 4], 5], 6])) # [1, 2, 3, 4, 5, 6]
5. Многопоточность и асинхронность
GIL и потоки
В: Что такое GIL?
Global Interpreter Lock — мьютекс, который позволяет только одному потоку выполнять Python-байткод в любой момент времени.
Последствия: многопоточность в Python не даёт ускорения для CPU-bound задач.
В: Когда использовать threading, когда multiprocessing?
- threading — для I/O-bound задач (сеть, диск). GIL отпускается во время ожидания I/O.
- multiprocessing — для CPU-bound задач. Отдельные процессы, свой GIL у каждого.
# I/O-bound: скачивание файлов
import threading
def download(url):
# requests.get(url) отпускает GIL
pass
threads = [threading.Thread(target=download, args=(url,)) for url in urls]
# CPU-bound: обработка данных
import multiprocessing
def process(data):
# тяжёлые вычисления
pass
with multiprocessing.Pool(4) as pool:
results = pool.map(process, data_chunks)
В: Что такое ThreadPoolExecutor?
Высокоуровневый API для работы с потоками.
from concurrent.futures import ThreadPoolExecutor, as_completed
def fetch(url):
return requests.get(url).text
with ThreadPoolExecutor(max_workers=10) as executor:
futures = {executor.submit(fetch, url): url for url in urls}
for future in as_completed(futures):
url = futures[future]
try:
data = future.result()
except Exception as e:
print(f"{url} failed: {e}")
Asyncio
В: Как работает asyncio?
Event loop + корутины. Один поток, но переключение между задачами во время await.
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
results = await asyncio.gather(*tasks)
return results
asyncio.run(main())
В: Разница между asyncio.gather и asyncio.wait?
# gather — ждёт все, возвращает результаты в порядке вызова
results = await asyncio.gather(coro1(), coro2(), coro3())
# wait — больше контроля
done, pending = await asyncio.wait(
tasks,
return_when=asyncio.FIRST_COMPLETED # или ALL_COMPLETED
)
В: Как создать асинхронный генератор?
async def async_range(n):
for i in range(n):
await asyncio.sleep(0.1)
yield i
async def main():
async for num in async_range(10):
print(num)
6. Работа с базами данных
В: ORM vs Raw SQL — когда что?
ORM (SQLAlchemy, Django ORM):
- Быстрая разработка
- Безопасность (защита от SQL injection)
- Миграции
- Портируемость между БД
Raw SQL:
- Сложные запросы
- Максимальная производительность
- Специфичные фичи БД
В: Что такое N+1 проблема?
# N+1 запросов
users = User.query.all() # 1 запрос
for user in users:
print(user.posts) # N запросов
# Решение: eager loading
users = User.query.options(joinedload(User.posts)).all() # 1-2 запроса
В: Как работает connection pool?
Пул переиспользуемых соединений. Создание соединения дорогое, пул амортизирует эту стоимость.
from sqlalchemy import create_engine
engine = create_engine(
"postgresql://user:pass@localhost/db",
pool_size=5, # постоянные соединения
max_overflow=10, # дополнительные при нагрузке
pool_timeout=30, # ожидание свободного соединения
pool_recycle=1800 # пересоздание старых соединений
)
7. Тестирование
В: unittest vs pytest?
pytest — современный стандарт:
- Меньше boilerplate
- Мощные fixtures
- Параметризация
- Плагины
# unittest
class TestMath(unittest.TestCase):
def test_add(self):
self.assertEqual(add(2, 3), 5)
# pytest
def test_add():
assert add(2, 3) == 5
# pytest с параметризацией
@pytest.mark.parametrize("a,b,expected", [
(2, 3, 5),
(-1, 1, 0),
(0, 0, 0),
])
def test_add_parametrized(a, b, expected):
assert add(a, b) == expected
В: Что такое mock? Когда использовать?
Замена реальных объектов для изоляции тестов.
from unittest.mock import Mock, patch
# Мокаем внешний API
@patch('mymodule.requests.get')
def test_fetch_data(mock_get):
mock_get.return_value.json.return_value = {"data": "test"}
result = fetch_data("http://api.com")
assert result == {"data": "test"}
mock_get.assert_called_once_with("http://api.com")
В: Что такое fixture в pytest?
Подготовка данных для тестов. Переиспользуемая, с разными scope.
@pytest.fixture
def db_session():
session = create_session()
yield session # тест выполняется здесь
session.rollback()
session.close()
@pytest.fixture(scope="module")
def test_data():
return load_test_data()
def test_query(db_session, test_data):
result = db_session.query(User).filter_by(id=test_data["user_id"]).first()
assert result is not None
8. Архитектура и паттерны (Senior)
В: SOLID принципы — примеры на Python?
# Single Responsibility
class UserRepository: # только работа с БД
def save(self, user): ...
class UserValidator: # только валидация
def validate(self, user): ...
# Open/Closed
class Discount(ABC):
@abstractmethod
def calculate(self, price): ...
class PercentDiscount(Discount):
def calculate(self, price):
return price * 0.9
# Liskov Substitution
# Подклассы должны быть взаимозаменяемы с базовым классом
# Interface Segregation
class Printable(Protocol):
def print(self): ...
class Scannable(Protocol):
def scan(self): ...
# Dependency Inversion
class OrderService:
def __init__(self, repository: Repository): # зависимость от абстракции
self.repository = repository
В: Паттерн Singleton на Python?
# Способ 1: декоратор
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
# Способ 2: метакласс
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]
# Способ 3: модуль (рекомендуется в Python)
# config.py — сам по себе singleton
В: Dependency Injection в Python?
# Простой DI через конструктор
class OrderService:
def __init__(self, repository, notifier):
self.repository = repository
self.notifier = notifier
# С использованием библиотеки (dependency-injector)
from dependency_injector import containers, providers
class Container(containers.DeclarativeContainer):
config = providers.Configuration()
db = providers.Singleton(Database, url=config.db_url)
repository = providers.Factory(UserRepository, db=db)
service = providers.Factory(UserService, repository=repository)
В: Как организовать большой Python-проект?
myproject/
├── src/
│ └── myproject/
│ ├── __init__.py
│ ├── domain/ # бизнес-логика
│ │ ├── models.py
│ │ └── services.py
│ ├── infrastructure/ # БД, внешние сервисы
│ │ ├── database.py
│ │ └── external_api.py
│ ├── api/ # HTTP endpoints
│ │ └── routes.py
│ └── config.py
├── tests/
│ ├── unit/
│ └── integration/
├── pyproject.toml
└── README.md
9. Алгоритмические задачи
Задача 1: Two Sum
def two_sum(nums: list[int], target: int) -> list[int]:
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
Задача 2: Valid Parentheses
def is_valid(s: str) -> bool:
stack = []
pairs = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in '([{':
stack.append(char)
elif char in ')]}':
if not stack or stack.pop() != pairs[char]:
return False
return len(stack) == 0
Задача 3: Merge Intervals
def merge(intervals: list[list[int]]) -> list[list[int]]:
intervals.sort(key=lambda x: x[0])
result = []
for interval in intervals:
if not result or result[-1][1] < interval[0]:
result.append(interval)
else:
result[-1][1] = max(result[-1][1], interval[1])
return result
Задача 4: LRU Cache
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity: int):
self.cache = OrderedDict()
self.capacity = capacity
def get(self, key: int) -> int:
if key not in self.cache:
return -1
self.cache.move_to_end(key)
return self.cache[key]
def put(self, key: int, value: int) -> None:
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False)
Задача 5: Binary Search
def binary_search(nums: list[int], target: int) -> int:
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
Как ENIGMA AI помогает на собеседовании
Даже зная все эти вопросы, легко забыть детали под стрессом. ENIGMA AI работает как подстраховка:
- Мгновенные подсказки — забыли синтаксис декоратора или как работает GIL
- Помощь с кодом — подскажет реализацию алгоритма
- Stealth режим — невидим при шаринге экрана
Попробовать ENIGMA AI бесплатно →
FAQ
Сколько вопросов задают на собеседовании?
Обычно 5-10 теоретических вопросов + 1-2 практические задачи. Для Senior добавляется System Design.
Нужно ли знать всё из этого списка?
Для Junior — базовые разделы (1-4). Middle должен уверенно отвечать на 70% вопросов. Senior — на 90%+.
Как отвечать, если не знаешь?
«С этим конкретно не работал, но исходя из общих принципов, думаю, что…» Покажите ход мыслей.
Этот гайд покрывает 90% вопросов на реальных собеседованиях. Сохраните в закладки и возвращайтесь при подготовке.
Похожие статьи

Собеседование в Ozon Tech: этапы, вопросы и как подготовиться

Собеседование в VK: этапы, вопросы и как подготовиться

Собеседование в Авито: этапы, вопросы и как подготовиться
