ENIGMA AI
ENIGMA AI
Собеседование 12 мин чтения

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

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)
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% вопросов на реальных собеседованиях. Сохраните в закладки и возвращайтесь при подготовке.

Похожие статьи

Все статьи