50 вопросов
🐍 ТОП 50 вопросов на собеседование Python-разработчика
Простые ответы с примерами — понятно даже ребёнку
📦 Структуры данных и типы
1. Что такое список (list) и кортеж (tuple)? Чем отличаются?
Список — это как корзина покупок: можно добавлять, убирать, менять товары.
Кортеж — это как запаянная посылка: содержимое нельзя изменить.
lst = [1, 2, 3] # список — изменяемый
lst[0] = 99 # ✅ можно
tpl = (1, 2, 3) # кортеж — неизменяемый
tpl[0] = 99 # ❌ TypeError
Когда использовать кортеж? Когда данные не должны меняться: координаты, RGB-цвет, день рождения.
2. Что такое словарь (dict)?
Словарь — это телефонная книга: у каждого имени (ключа) есть номер (значение). Поиск за O(1) — мгновенно, потому что внутри хэш-таблица.
phone_book = {"Маша": "123-45-67", "Вася": "987-65-43"}
print(phone_book["Маша"]) # "123-45-67"
3. Что такое множество (set)?
Множество — это список уникальных элементов без порядка. Как список гостей: каждый человек упоминается один раз.
guests = {"Маша", "Вася", "Маша"} # "Маша" добавится один раз
print(guests) # {"Маша", "Вася"}
# Удаление дубликатов из списка:
unique = list(set([1, 2, 2, 3, 3])) # [1, 2, 3]
4. Чем is отличается от ==?
==сравнивает значения (содержимое)isсравнивает идентичность (один ли это объект в памяти)
a = [1, 2, 3]
b = [1, 2, 3]
a == b # True — значения одинаковые
a is b # False — это два разных объекта в памяти
c = a
a is c # True — c и a ссылаются на один объект
Правило: Используй
isтолько для сравнения сNone,True,False.
5. Что такое None?
None — это "ничего". Как пустая коробка. Используется когда функция ничего не возвращает или значение отсутствует.
def greet(name=None):
if name is None:
return "Привет, незнакомец!"
return f"Привет, {name}!"
6. Как сделать список уникальным (без повторений)?
numbers = [1, 2, 2, 3, 3, 1]
# Быстро, но порядок не гарантирован:
unique = list(set(numbers))
# С сохранением порядка (Python 3.7+):
unique = list(dict.fromkeys(numbers)) # [1, 2, 3]
7. Что такое срезы (slices)?
Срезы — как вырезать кусок из багета: указываешь с какого места и до какого.
s = [0, 1, 2, 3, 4, 5]
s[1:4] # [1, 2, 3] — с 1 по 4 (не включая 4)
s[:3] # [0, 1, 2] — первые 3
s[-2:] # [4, 5] — последние 2
s[::-1] # [5, 4, 3, 2, 1, 0] — перевернуть
🔧 Функции
8. Что такое *args и **kwargs?
*args— принимает любое количество позиционных аргументов (кортеж)**kwargs— принимает любое количество именованных аргументов (словарь)
def show(*args, **kwargs):
print(args) # (1, 2, 3)
print(kwargs) # {"name": "Вася", "age": 25}
show(1, 2, 3, name="Вася", age=25)
Аналогия:
*args— принести сколько угодно яблок,**kwargs— принести яблоки с ярлыками.
9. Почему нельзя использовать изменяемые объекты как аргументы по умолчанию?
Значение по умолчанию создаётся один раз при определении функции, а не при каждом вызове. Список живёт между вызовами!
# ❌ Плохо:
def add(item, lst=[]):
lst.append(item)
return lst
add(1) # [1]
add(2) # [1, 2] — список сохранился!
# ✅ Хорошо:
def add(item, lst=None):
if lst is None:
lst = []
lst.append(item)
return lst
10. Что такое лямбда-функция?
Лямбда — это маленькая анонимная функция в одну строку. Как записка вместо письма.
# Обычная функция:
def double(x):
return x * 2
# Лямбда:
double = lambda x: x * 2
# Чаще используется "на месте":
numbers = [3, 1, 4, 1, 5]
numbers.sort(key=lambda x: -x) # сортировка по убыванию
11. Что такое замыкание (closure)?
Замыкание — это функция, которая "запоминает" переменные из внешней функции, даже после её завершения.
def make_greeter(greeting):
def greet(name):
return f"{greeting}, {name}!" # запомнила greeting
return greet
hello = make_greeter("Привет")
hi = make_greeter("Хай")
hello("Маша") # "Привет, Маша!"
hi("Вася") # "Хай, Вася!"
12. Что такое декоратор?
Декоратор — это обёртка для функции. Как коробочка вокруг подарка: подарок тот же, но с украшением.
def logger(func):
def wrapper(*args, **kwargs):
print(f"Вызов: {func.__name__}")
result = func(*args, **kwargs)
print(f"Готово!")
return result
return wrapper
@logger
def add(a, b):
return a + b
add(2, 3)
# Вызов: add
# Готово!
13. Зачем нужен functools.wraps?
Без wraps задекорированная функция теряет своё имя и документацию. wraps сохраняет "паспорт" оригинальной функции.
from functools import wraps
def decorator(func):
@wraps(func) # сохраняем имя и __doc__
def wrapper(*args):
return func(*args)
return wrapper
@decorator
def my_func():
"""Моя функция"""
pass
print(my_func.__name__) # "my_func" (без @wraps было бы "wrapper")
🔄 Итераторы и генераторы
14. Чем итерируемый объект отличается от итератора?
- Итерируемый объект — можно обойти в
for(список, строка, словарь) - Итератор — объект с методами
__iter__и__next__, выдаёт по одному элементу
lst = [1, 2, 3] # итерируемый объект
it = iter(lst) # итератор
next(it) # 1
next(it) # 2
next(it) # 3
next(it) # StopIteration
15. Что такое генератор и зачем он нужен?
Генератор — это функция с yield. Она не создаёт весь список сразу, а выдаёт по одному элементу. Экономит память!
# Список: создаёт 1 000 000 чисел в памяти сразу
nums = [x * 2 for x in range(1_000_000)]
# Генератор: создаёт числа по одному, память почти не тратит
def gen():
for x in range(1_000_000):
yield x * 2
# Аналогия: список — весь фильм скачан, генератор — стриминг
16. Чем [x for x in y] отличается от (x for x in y)?
[...]— список, создаётся сразу целиком (занимает память)(...)— генераторное выражение, ленивое (элементы по одному)
lst = [x**2 for x in range(10)] # список из 10 чисел в памяти
gen = (x**2 for x in range(10)) # генератор — числа по требованию
🏛️ ООП
17. Что такое __init__?
__init__ — это конструктор класса. Вызывается автоматически при создании объекта. Как заполнение анкеты при регистрации.
class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed
my_dog = Dog("Шарик", "Лабрадор")
print(my_dog.name) # "Шарик"
18. В чём разница между __init__ и __new__?
__new__— создаёт объект (выделяет память)__init__— инициализирует объект (заполняет данными)
Обычно переопределяют только
__init__.__new__нужен редко — например, для синглтонов.
19. Что такое self?
self — это ссылка на сам объект. Как слово "я" для человека. Позволяет методу обращаться к данным своего объекта.
class Cat:
def __init__(self, name):
self.name = name # self = "этот конкретный кот"
def speak(self):
return f"{self.name} говорит: Мяу!"
cat = Cat("Мурзик")
cat.speak() # "Мурзик говорит: Мяу!"
20. Что такое наследование?
Наследование — как ребёнок, который получает черты родителей, но может добавить свои.
class Animal:
def breathe(self):
return "Дышу воздухом"
class Dog(Animal): # Dog наследует Animal
def bark(self):
return "Гав!"
dog = Dog()
dog.breathe() # ✅ унаследовал от Animal
dog.bark() # ✅ собственный метод
21. Что такое super()?
super() — вызов метода родительского класса. Как сказать "сначала сделай то, что делает родитель, потом добавлю своё".
class Animal:
def __init__(self, name):
self.name = name
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # вызываем Animal.__init__
self.breed = breed # добавляем своё
dog = Dog("Шарик", "Хаски")
22. Что такое MRO (порядок разрешения методов)?
MRO — порядок, в котором Python ищет метод при множественном наследовании. Смотрит слева направо, снизу вверх.
class A:
def hello(self):
return "A"
class B(A):
pass
class C(A):
def hello(self):
return "C"
class D(B, C):
pass
D().hello() # "C" — Python ищет по цепочке D → B → C → A
print(D.__mro__) # (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, ...)
23. Что такое магические методы (__dunder__)?
Методы с двойным подчёркиванием — "магия" Python. Они вызываются автоматически при определённых операциях.
class Box:
def __init__(self, items):
self.items = items
def __len__(self): # вызывается при len(box)
return len(self.items)
def __str__(self): # вызывается при print(box)
return f"Коробка с {len(self.items)} предметами"
box = Box([1, 2, 3])
len(box) # 3
print(box) # "Коробка с 3 предметами"
24. Что такое __slots__?
__slots__ фиксирует набор атрибутов объекта. Экономит память при создании миллионов объектов.
class Point:
__slots__ = ('x', 'y') # только эти атрибуты разрешены
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
p.z = 3 # ❌ AttributeError — z не разрешён
25. Что такое @property?
@property позволяет вызывать метод как обычный атрибут (без скобок). Удобно для валидации.
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def fahrenheit(self): # вызывается как t.fahrenheit
return self._celsius * 9/5 + 32
t = Temperature(100)
print(t.fahrenheit) # 212 (без скобок!)
💾 Память и производительность
26. Как Python управляет памятью?
Python использует подсчёт ссылок: каждый объект знает, сколько переменных на него ссылаются. Как только ссылок 0 — объект удаляется. Для циклических ссылок есть Garbage Collector.
a = [1, 2, 3] # ссылок: 1
b = a # ссылок: 2
del a # ссылок: 1
del b # ссылок: 0 → объект удалён
27. Что такое GIL?
GIL (Global Interpreter Lock) — замок, который позволяет в один момент времени выполняться только одному потоку Python. Это значит, что настоящего параллелизма в потоках нет.
Аналогия: Одна кассирша в супермаркете — в каждый момент обслуживает только одного покупателя, даже если очередей несколько.
- ✅ Потоки подходят для задач с ожиданием (сеть, файлы)
- ❌ Потоки не ускоряют математические вычисления
- ✅ Для вычислений — используй
multiprocessing
28. В чём разница между потоками и процессами?
Потоки (threading) |
Процессы (multiprocessing) |
|
|---|---|---|
| Память | Общая | Раздельная |
| GIL | Есть (ограничение) | Нет |
| Для чего | I/O задачи | CPU задачи |
| Создание | Быстро | Медленнее |
29. Что такое deepcopy и copy?
import copy
original = [[1, 2], [3, 4]]
shallow = copy.copy(original) # поверхностная копия
deep = copy.deepcopy(original) # глубокая копия
original[0][0] = 99
print(shallow[0][0]) # 99 — изменилось! (ссылается на те же вложенные списки)
print(deep[0][0]) # 1 — не изменилось (полностью независимая копия)
Аналогия:
copy— скопировать папку со ссылками на файлы;deepcopy— скопировать папку вместе со всеми файлами.
⚠️ Исключения
30. Как работает обработка исключений?
try:
result = 10 / 0 # здесь произойдёт ошибка
except ZeroDivisionError:
print("Делить на ноль нельзя!")
except Exception as e:
print(f"Ошибка: {e}")
else:
print("Всё хорошо!") # если ошибок не было
finally:
print("Выполняется всегда") # всегда
31. Как создать своё исключение?
class InsufficientFundsError(Exception):
def __init__(self, amount):
self.amount = amount
super().__init__(f"Не хватает {amount} рублей")
def withdraw(balance, amount):
if amount > balance:
raise InsufficientFundsError(amount - balance)
return balance - amount
🔀 Функциональное программирование
32. Что делают map, filter, reduce?
from functools import reduce
numbers = [1, 2, 3, 4, 5]
# map — применить функцию к каждому элементу
doubled = list(map(lambda x: x * 2, numbers)) # [2, 4, 6, 8, 10]
# filter — оставить только подходящие
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4]
# reduce — свернуть в одно значение
total = reduce(lambda acc, x: acc + x, numbers) # 15
33. Что такое lru_cache?
lru_cache запоминает результаты функции. При повторном вызове с теми же аргументами — возвращает из кэша, не считает заново.
from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
fibonacci(100) # мгновенно, результаты кэшированы
🌐 Async / Await
34. Что такое async/await?
async/await — инструмент для асинхронного кода. Пока одна задача ждёт ответа (сеть, БД), другие задачи продолжают работать.
import asyncio
async def fetch_data(url):
print(f"Загружаю {url}...")
await asyncio.sleep(1) # ждём, но не блокируем
return f"Данные с {url}"
async def main():
# Запускаем оба запроса одновременно!
results = await asyncio.gather(
fetch_data("сайт1.ру"),
fetch_data("сайт2.ру"),
)
asyncio.run(main())
Аналогия: Официант принял заказ у стола 1, пошёл к столу 2, потом к столу 3 — а не стоит и ждёт пока приготовят еду.
🧩 Модули и пакеты
35. Как Python ищет модули при импорте?
Python ищет в таком порядке:
1. Встроенные модули (os, sys)
2. Папка с запускаемым скриптом
3. Переменная окружения PYTHONPATH
4. Стандартные системные пути
import sys
print(sys.path) # список путей поиска
36. Что такое __name__ == "__main__"?
Защита от выполнения кода при импорте. Код внутри if запустится только если файл запущен напрямую.
def add(a, b):
return a + b
if __name__ == "__main__":
print(add(2, 3)) # выполнится только при python my_file.py
# но НЕ при import my_file
🗃️ Базы данных
37. Что такое транзакция и свойства ACID?
Транзакция — группа операций, которые выполняются как одно целое.
| Свойство | Что значит | Пример |
|---|---|---|
| Atomicity (Атомарность) | Всё или ничего | Перевод денег: списать И зачислить |
| Consistency (Согласованность) | Данные всегда корректны | Баланс не может быть отрицательным |
| Isolation (Изолированность) | Транзакции не мешают друг другу | Два кассира не выдадут одну купюру |
| Durability (Долговечность) | Зафиксированные данные не потеряются | Даже после сбоя питания |
38. Какие бывают JOIN-ы?
-- INNER JOIN — только совпадения в обеих таблицах
SELECT * FROM orders
INNER JOIN customers ON orders.customer_id = customers.id;
-- LEFT JOIN — все из левой + совпадения из правой
SELECT * FROM customers
LEFT JOIN orders ON customers.id = orders.customer_id;
-- FULL JOIN — все записи из обеих таблиц
SELECT * FROM customers
FULL JOIN orders ON customers.id = orders.customer_id;
Аналогия: Два списка гостей. INNER — кто есть в обоих. LEFT — все из первого + кто есть во втором.
🔑 Паттерны проектирования
39. Что такое Singleton?
Singleton гарантирует, что у класса есть только один экземпляр.
class Database:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
db1 = Database()
db2 = Database()
db1 is db2 # True — один и тот же объект!
40. Что такое декоратор как паттерн?
Добавляет функциональность объекту, не изменяя его код. Как надеть пальто — человек тот же, но теперь ему тепло.
🌍 HTTP и веб
41. Что такое REST?
REST — соглашение о том, как строить API. Основные принципы:
- Ресурсы в URL: /users/42
- Действия через HTTP-методы: GET, POST, PUT, DELETE
- Без состояния: каждый запрос самодостаточен
GET /users — получить список
POST /users — создать пользователя
GET /users/42 — получить пользователя #42
PUT /users/42 — обновить пользователя #42
DELETE /users/42 — удалить пользователя #42
42. Что такое HTTP-коды состояния?
| Диапазон | Значение | Примеры |
|---|---|---|
2xx |
Успех | 200 OK, 201 Created |
3xx |
Перенаправление | 301 Moved, 304 Not Modified |
4xx |
Ошибка клиента | 400 Bad Request, 404 Not Found |
5xx |
Ошибка сервера | 500 Internal Error, 503 Unavailable |
43. Что такое JWT?
JWT (JSON Web Token) — компактный токен для передачи данных и аутентификации. Состоит из трёх частей: заголовок, данные, подпись.
eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo0Mn0.SflKxwRJSMeKKF2QT4fwpMeJf
заголовок данные подпись
🧪 Тестирование
44. Что такое Unit-тесты?
Unit-тест проверяет одну маленькую функцию в изоляции. Как тест одной детали машины на заводе.
import pytest
def add(a, b):
return a + b
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0
assert add(0, 0) == 0
45. Что такое Mock?
Mock — заглушка, которая имитирует реальный объект. Нужна чтобы тестировать код без реальных запросов к базе или API.
from unittest.mock import patch
def get_user(user_id):
# В реальности идёт запрос к базе
return database.find(user_id)
def test_get_user():
with patch('database.find') as mock_find:
mock_find.return_value = {"id": 1, "name": "Маша"}
user = get_user(1)
assert user["name"] == "Маша"
⚙️ Git и инструменты
46. Что такое git rebase vs git merge?
merge— сливает ветки, сохраняя всю историю коммитов (добавляет "merge commit")rebase— переставляет твои коммиты поверх ветки (история линейная, чище)
merge: A--B--C--M (M = merge commit)
\ /
D--E
rebase: A--B--C--D'--E' (коммиты D,E перенесены наверх)
47. Что такое CI/CD?
- CI (Continuous Integration) — автоматически запускает тесты при каждом коммите
- CD (Continuous Delivery) — автоматически доставляет изменения на сервер после успешных тестов
Аналогия: CI — контролёр качества на конвейере. CD — автоматический грузовик доставки.
🏗️ SOLID и принципы
48. Расскажи о SOLID простыми словами
| Принцип | Простое объяснение |
|---|---|
| S — Single Responsibility | Один класс = одна задача (повар готовит, официант подаёт) |
| O — Open/Closed | Расширяй, не ломая старое (добавляй плагины, не трогая ядро) |
| L — Liskov Substitution | Наследник должен работать везде, где работает родитель |
| I — Interface Segregation | Не заставляй реализовывать ненужные методы |
| D — Dependency Inversion | Зависеть от абстракций, не от конкретных классов |
49. Что такое DRY и KISS?
DRY (Don't Repeat Yourself) — не копируй код, выноси в функцию.
# ❌ Нарушение DRY
price_usd = 100 * 0.18 + 100 # с НДС
price_eur = 200 * 0.18 + 200
# ✅ DRY
def add_tax(price, rate=0.18):
return price * rate + price
KISS (Keep It Simple, Stupid) — не усложняй без нужды. Простой код лучше умного.
50. Что такое технический долг?
Технический долг — это когда ради скорости делаешь "грязное" решение, понимая что потом придётся переделать. Как взять кредит: удобно сейчас, но платишь проценты потом.
Признаки накопленного долга:
- Страшно трогать старый код
- Любое изменение ломает что-то ещё
- Новые разработчики долго разбираются в коде
Как бороться:
- Рефакторинг в рамках каждой задачи
- Выделять время в спринте на технический долг
- Писать тесты перед изменением legacy-кода