Skip to content

Найпопулярніші запитання та відповіді на співбесіді з Python

License

Notifications You must be signed in to change notification settings

DevLoversTeam/python-interview-questions

Python Python logo

Найпопулярніші запитання та відповіді на співбесіді з Python

1. Що таке Python і які його основні особливості?

Python

Python — це високорівнева мова загального призначення з акцентом на читабельність, швидку розробку та великий стандартний інструментарій.

Основні особливості:

  • Простий синтаксис: код легко читати й підтримувати.
  • Інтерпретована модель виконання: швидкий цикл змін без етапу ручної компіляції.
  • Кросплатформеність: один код працює на Linux, macOS, Windows.
  • Мультипарадигменність: procedural, OOP, functional підходи в одному проєкті.
  • Сильна екосистема: pip, venv, pyproject.toml, тисячі готових бібліотек.
  • Сучасні можливості Python 3.14+: type hints, dataclass, async/await, match/case, генератори, context managers.

Приклад production-style функції:

from dataclasses import dataclass

@dataclass(slots=True)
class User:
    name: str
    active: bool

def process_users(users: list[User]) -> list[str]:
    return [user.name for user in users if user.active]

Коротко:

  • Python прискорює розробку завдяки простому синтаксису та великій стандартній бібліотеці.
  • Мова підходить для backend, automation, data/ML, тестування та DevOps.
  • У Python 3.14+ ключові практики: type hints, async IO, сучасний standard library.
2. Які ключові переваги Python?

Python

Ключові переваги Python в продакшені:

  • висока швидкість розробки через лаконічний синтаксис;
  • велика стандартна бібліотека (pathlib, itertools, collections, asyncio);
  • зріла екосистема пакетів для backend, data, automation, тестування;
  • переносимість коду між ОС;
  • хороша підтримка типізації (typing, mypy, pyright) і сучасних практик.

Коротко:

  • Python зменшує time-to-market.
  • Дає багато готових інструментів без додаткових залежностей.
  • Підходить і для MVP, і для масштабованих сервісів.
3. Чим Python відрізняється від компільованих мов програмування?

Python

Python зазвичай виконується інтерпретатором: код компілюється у байткод і виконується в рантаймі (CPython VM). У компільованих мовах (C/C++, Rust) переважно є попередня компіляція у машинний код.

Практичні наслідки:

  • Python швидше в розробці та прототипуванні.
  • Нативні компільовані мови зазвичай швидші у CPU-bound задачах.
  • У Python продуктивність часто покращують алгоритмами, профілюванням і C-розширеннями.

Коротко:

  • Python оптимізує швидкість розробки.
  • Компіляція в машинний код зазвичай дає кращу raw-продуктивність.
  • Вибір залежить від домену та вимог до latency/throughput.
4. Що означає динамічна типізація в Python?

Python

Динамічна типізація означає, що тип прив'язаний до об'єкта, а не до імені змінної. Ім'я може посилатися на значення різних типів у різний час виконання.

value = 10        # int
value = "10"      # str

Типи перевіряються під час виконання, тому помилки типів виникають у runtime, якщо не використовувати статичний аналізатор типів.

Коротко:

  • У Python тип має об'єкт, не змінна.
  • Тип може змінюватися при перевизначенні імені.
  • Type hints додають контроль до запуску коду.
5. Що означає строгість типів (strong typing) у Python?

Python

Strong typing у Python означає, що інтерпретатор не виконує небезпечні неявні перетворення між несумісними типами.

1 + "2"  # TypeError

Для операцій між різними типами потрібне явне приведення:

1 + int("2")  # 3

Коротко:

  • Python динамічно типізований, але строгий щодо сумісності типів.
  • Небезпечні неявні конверсії блокуються помилкою.
  • Явне приведення робить поведінку передбачуваною.
6. Що таке REPL і коли його використовують?

Python

REPL (Read-Eval-Print Loop) це інтерактивний режим, де ви вводите вираз, отримуєте результат одразу і швидко перевіряєте гіпотези.

Застосування:

  • перевірити API бібліотеки;
  • швидко протестувати вираз або алгоритм;
  • дослідити дані перед імплементацією в модулі.

Інструменти: стандартний python, ipython, ptpython.

Коротко:

  • REPL дає найшвидший фідбек-цикл.
  • Зручний для експериментів і дебагу.
  • Не замінює тести, але скорочує час перевірки ідей.
7. Що таке literals у Python, наведіть приклади різних типів literals?

Python

Literal це фіксоване значення, записане прямо в коді.

name = "Ada"                  # str literal
count = 42                    # int literal
ratio = 3.14                  # float literal
enabled = True                # bool literal
items = [1, 2, 3]             # list literal
config = {"retries": 3}       # dict literal
flags = {"a", "b"}            # set literal
point = (10, 20)              # tuple literal
raw = b"abc"                  # bytes literal

Коротко:

  • Literals це вбудовані константні значення в коді.
  • Кожен базовий тип має свій синтаксис літерала.
  • Вони часто використовуються для ініціалізації даних.
8. Що таке keywords у Python?

Python

Keywords це зарезервовані слова мови, які мають спеціальне синтаксичне призначення і не можуть бути іменами змінних.

Приклади: if, for, class, def, match, case, try, except, async, await.

Переглянути список:

import keyword
print(keyword.kwlist)

Коротко:

  • Keywords формують синтаксис Python.
  • Їх не можна використовувати як ідентифікатори.
  • Список доступний через модуль keyword.
9. Що таке змінна в Python?

Python

Змінна в Python це ім'я (reference), яке посилається на об'єкт у пам'яті. Присвоєння зв'язує ім'я з об'єктом, а не "кладе значення в коробку".

x = [1, 2]
y = x
y.append(3)
# x і y посилаються на один список

Коротко:

  • Змінна це посилання на об'єкт.
  • Кілька імен можуть посилатися на один mutable-об'єкт.
  • Це важливо для розуміння копіювання і побічних ефектів.
10. Що таке `pass` і `...` (Ellipsis) у Python?

Python

pass це оператор-заглушка: нічого не робить, але дозволяє синтаксично коректний порожній блок.

... (Ellipsis) це окремий об'єкт Ellipsis. Часто використовується як маркер "ще не реалізовано", у type hints та бібліотеках (наприклад NumPy).

def feature() -> None:
    pass

PLACEHOLDER = ...

Коротко:

  • pass потрібен для порожніх блоків коду.
  • ... це значення-об'єкт, а не ключове слово.
  • Обидва використовують як тимчасові заглушки з різною семантикою.
11. Що таке PEP і як він впливає на розвиток Python?

Python

PEP (Python Enhancement Proposal) це офіційний документ, який описує нову фічу, стандарт або процес у екосистемі Python.

Через PEP проходять:

  • обговорення дизайну;
  • технічна аргументація;
  • рішення про прийняття/відхилення;
  • стандартизація практик.

Приклади: PEP 8 (style), PEP 484 (type hints), PEP 634 (pattern matching).

Коротко:

  • PEP це механізм еволюції мови та інструментів.
  • Він робить зміни прозорими й формалізованими.
  • Багато сучасних практик Python закріплені саме через PEP.
12. Що таке PEP 8 і навіщо він потрібен?

Python

PEP 8 це офіційний стиль-гайд для Python-коду: іменування, форматування, відступи, імпорти, довжина рядків, читабельність конструкцій.

Навіщо потрібен:

  • код має однаковий стиль у команді;
  • швидше code review;
  • менше шуму в diff;
  • вища підтримуваність.

На практиці стиль автоматизують через ruff format або black + ruff.

Коротко:

  • PEP 8 стандартизує стиль Python-коду.
  • Це про читабельність і підтримку, не про продуктивність.
  • Дотримання краще автоматизувати інструментами.
13. Які нові можливості з'явилися у сучасних версіях Python (3.10+)?

Python

Ключові можливості сучасного Python:

  • match/case (structural pattern matching);
  • покращений синтаксис і повідомлення про помилки;
  • union-типи через | (int | None);
  • typing.Self, typing.TypeAlias, typing.override;
  • generics у стилі PEP 695 (class Box[T]: ..., def f[T](...) -> ...);
  • tomllib у стандартній бібліотеці.

Практично це зменшує boilerplate і покращує надійність типізованого коду.

Коротко:

  • Python 3.10+ суттєво покращив синтаксис і typing.
  • match/case і сучасний typing найпомітніше впливають на код.
  • Нові фічі варто використовувати в новому коді за замовчуванням.
14. Що таке docstring у Python?

Python

Docstring це рядок документації, перший вираз у модулі, класі або функції. Він доступний через __doc__ і використовується IDE, help(), генераторами docs.

def normalize_email(email: str) -> str:
    """Return lowercase email without surrounding spaces."""
    return email.strip().lower()

Рекомендовано описувати: що робить функція, аргументи, повернення, винятки.

Коротко:

  • Docstring це вбудована документація в коді.
  • Працює як джерело для help() та автогенерації docs.
  • Якісний docstring зменшує потребу читати реалізацію.
15. Як працюють відступи (indentation) у Python?

Python

У Python відступи визначають блоки коду (тіло if, for, def, class). Тобто відступ є частиною синтаксису, а не лише стилем.

if is_ready:
    run_task()
else:
    stop_task()

Змішування tab і spaces може призвести до IndentationError або некоректної структури блоку.

Коротко:

  • Відступ у Python задає структуру програми.
  • Неправильний відступ ламає виконання.
  • Використовуйте стабільний стиль (4 пробіли).
16. Скільки пробілів потрібно для відступу відповідно до PEP8 та чому важливо дотримуватись однакового відступу?

Python

PEP 8 рекомендує 4 пробіли на один рівень відступу.

Чому це критично:

  • однакова структура блоку в усьому проєкті;
  • передбачуваний вигляд у різних редакторах;
  • менше помилок через tab/space конфлікти;
  • чистіший diff у Git.

Коротко:

  • Стандартний відступ: 4 пробіли.
  • Єдиний стиль прибирає клас помилок форматування.
  • Це напряму покращує читабельність і підтримку коду.
17. У чому різниця між `ruff` і `black` щодо функціоналу та використання?

Python

black це форматер коду з фіксованими правилами.

ruff це швидкий linter, а також форматер і автофіксер багатьох правил (PEP8, імпорти, потенційні баги, спрощення синтаксису).

Практичні підходи:

  • або ruff check --fix + ruff format;
  • або ruff для lint + black для форматування.

Коротко:

  • black фокусується на форматуванні.
  • ruff закриває linting і частину автоправок.
  • У сучасних проєктах часто достатньо одного ruff.
18. Поясніть призначення type annotations у Python і наведіть приклад функції з type annotations.

Python

Type annotations описують очікувані типи аргументів і повернення. Вони покращують автодоповнення, статичний аналіз і читабельність API.

def process_users(users: list[dict[str, object]]) -> list[str]:
    return [str(user["name"]) for user in users if bool(user.get("active"))]

Анотації не виконують runtime-валідацію самі по собі, але допомагають знайти помилки на етапі CI через mypy/pyright.

Коротко:

  • Type hints документують контракт функції.
  • Дають раннє виявлення помилок через static checking.
  • Підвищують якість API у великих кодових базах.
19. Що таке debugging і чому це важливий навик для програмістів?

Python

Debugging це системний пошук причини некоректної поведінки коду та її усунення. Це не лише "знайти баг", а відтворити, локалізувати і перевірити fix.

Базовий цикл:

  • відтворити проблему;
  • звузити ділянку коду;
  • перевірити гіпотезу (логами/дебагером);
  • додати тест, що фіксує сценарій.

Коротко:

  • Debugging зменшує MTTR і кількість регресій.
  • Працює найкраще як процес, а не хаотичний пошук.
  • Завершення дебагу: fix + тест + постперевірка.
20. Поясніть призначення використання функції `print` для налагодження. Наведіть приклад ситуації, коли цей підхід корисний.

Python

print-debugging це швидкий спосіб перевірити значення змінних і порядок виконання без запуску складних інструментів.

Корисно, коли треба швидко перевірити гіпотезу в локальному скрипті:

def parse_port(raw: str) -> int:
    value = raw.strip()
    print(f"raw={raw!r} value={value!r}")
    return int(value)

Для продакшен-коду краще переходити на logging, щоб мати рівні логів і кероване виведення.

Коротко:

  • print підходить для короткої локальної діагностики.
  • Швидко показує стан змінних у проблемній точці.
  • Для стабільної діагностики в сервісі використовуйте logging.
21. Опишіть, як можна використовувати Python Debugger (PDB) для налагодження. Які переваги PDB у порівнянні з `print`?

Python

pdb дозволяє зупиняти виконання програми, крокувати по рядках, дивитися стан змінних і стек викликів.

Базове використання:

import pdb

def compute(x: int) -> int:
    pdb.set_trace()
    return x * 2

Ключові команди: n (next), s (step), c (continue), p expr (print expr), l (list), bt (backtrace).

Коротко:

  • pdb дає інтерактивний контроль виконання.
  • Ефективніший за print для складних сценаріїв.
  • Дозволяє діагностувати стан без засмічення коду логами.
22. Які стратегії можна застосовувати для налагодження циклів та функцій у Python?

Python

Практичні стратегії:

  • локалізувати баг через мінімальний відтворюваний приклад;
  • логувати інваріанти на ітераціях циклу;
  • ставити breakpoint на вхід/вихід функції;
  • перевіряти крайні випадки (порожні дані, None, великі обсяги);
  • покривати проблемний шлях тестом.

Для циклів корисно логувати індекс і ключові проміжні стани.

Коротко:

  • Починайте з відтворення і звуження проблеми.
  • Перевіряйте інваріанти і крайні умови.
  • Фіналізуйте фікс тестом, що ловить регресію.
23. Який вплив мають довготривалі функції на продуктивність програми?

Python

Довготривалі функції збільшують latency, блокують воркери/потоки і погіршують пропускну здатність сервісу.

Ризики:

  • повільні відповіді API;
  • таймаути;
  • зростання черг задач;
  • гірше використання CPU/IO.

Підхід: профілювати (cProfile), розбивати на менші кроки, використовувати генератори/стрімінг, виносити важкі обчислення в multiprocessing або фон.

Коротко:

  • Довгі функції напряму б'ють по latency.
  • Оптимізацію роблять на основі профілювання, не інтуїції.
  • Архітектурно краще розділяти CPU-bound і IO-bound роботу.
24. Що таке logging і чому його краще використовувати замість `print`?

Python

logging це стандартний механізм журналювання подій із рівнями (DEBUG, INFO, WARNING, ERROR, CRITICAL), форматами й обробниками (console, file).

Чому краще за print:

  • керовані рівні деталізації;
  • структурований формат;
  • централізована конфігурація;
  • можливість інтеграції з observability-стеком.

Коротко:

  • logging придатний для продакшену, print ні.
  • Рівні логів дають контроль шуму.
  • Логи мають бути структурованими й консистентними.
25. Що таке traceback?

Python

Traceback це стек викликів, який Python показує при винятку: де почалось виконання, через які функції пройшов код і де саме сталася помилка.

Використання:

  • швидко знайти файл/рядок джерела помилки;
  • зрозуміти шлях виконання до збою;
  • побудувати точний тест на відтворення.

Коротко:

  • Traceback це "маршрут" до помилки.
  • Найцінніша частина: останні кадри стека і тип винятку.
  • Аналіз traceback прискорює root-cause.
26. Які основні типи даних існують у Python?

Python

Основні built-in типи:

  • числові: int, float, complex;
  • логічний: bool;
  • рядки/байти: str, bytes, bytearray;
  • колекції: list, tuple, set, dict, frozenset;
  • спеціальні: NoneType (None).

Важливо розуміти mutability:

  • mutable: list, dict, set, bytearray;
  • immutable: int, str, tuple, frozenset.

Коротко:

  • Python має багатий набір базових типів "з коробки".
  • Вибір структури даних впливає на складність операцій.
  • Mutability визначає поведінку копіювання та побічні ефекти.
27. Яка різниця між звичайним діленням (`/`) і цілочисельним діленням (`//`) у Python?

Python

/ виконує звичайне ділення і завжди повертає float.

// виконує floor-ділення: повертає цілу частину вниз (до -∞), тип зазвичай int для цілих операндів.

7 / 2    # 3.5
7 // 2   # 3
-7 // 2  # -4

Коротко:

  • / -> дійсний результат.
  • // -> округлення вниз до цілого.
  • Для від'ємних чисел // не еквівалентне "обрізанню до нуля".
28. Поясніть використання оператора модуля (`%`) у Python. Як його можна використати щоб визначити, чи число парне або непарне?

Python

Оператор % повертає остачу від ділення.

Перевірка парності:

def is_even(n: int) -> bool:
    return n % 2 == 0

Якщо n % 2 == 0, число парне; інакше непарне.

Типові задачі: циклічні індекси, розбиття на групи, календарні обчислення.

Коротко:

  • % повертає remainder.
  • n % 2 це стандартна перевірка парності.
  • Часто використовується для циклічної логіки.
29. Як Python працює з великими цілими числами і які є обмеження при роботі з дуже великими числами?

Python

int у Python має довільну точність (arbitrary precision), тобто не обмежений 32/64-бітами як у багатьох мовах.

Обмеження практичні:

  • пам'ять процесу;
  • час обчислень на дуже великих значеннях;
  • вартість операцій зростає зі збільшенням кількості цифр.

Коротко:

  • Переповнення int у звичному сенсі немає.
  • Обмеження: ресурси машини, а не фіксований бітовий розмір.
  • Для великих обчислень важливі алгоритми і профілювання.
30. Яке значення має обробка ділення на нуль у Python, як запобігти `ZeroDivisionError` у коді?

Python

Ділення на нуль викликає ZeroDivisionError. Це треба обробляти явно, якщо дільник надходить із зовнішнього вводу або розраховується динамічно.

def safe_div(a: float, b: float) -> float | None:
    if b == 0:
        return None
    return a / b

У критичних сценаріях використовуйте try/except і логування контексту.

Коротко:

  • Ділення на нуль це контрольована runtime-помилка.
  • Найпростіше запобігання: перевірка дільника до операції.
  • Для зовнішніх даних додавайте захист і діагностику.
31. Опишіть використання модуля `random` у Python. Які найбільш поширені функції у цьому модулі?

Python

random використовується для псевдовипадкових значень у симуляціях, тестових даних, non-crypto задачах.

Поширені функції:

  • random() — число у [0.0, 1.0);
  • randint(a, b) — ціле в діапазоні;
  • randrange(start, stop, step) — як range, але випадковий елемент;
  • choice(seq) / choices(seq, k=...);
  • shuffle(list_) — перемішує список in-place;
  • sample(population, k) — унікальна вибірка.

Для криптографії використовуйте secrets, не random.

Коротко:

  • random підходить для звичайної прикладної випадковості.
  • Найчастіше: randint, choice, shuffle, sample.
  • Для security-sensitive задач потрібен secrets.
32. Як у Python працювати з числами з кращою точністю?

Python

Для фінансових і точних десяткових обчислень використовуйте decimal.Decimal, а не float.

from decimal import Decimal

total = Decimal("0.1") + Decimal("0.2")  # Decimal('0.3')

Для раціональних чисел корисний fractions.Fraction.

Коротко:

  • float зручний, але має похибки двійкового представлення.
  • Для точної десяткової арифметики обирайте Decimal.
  • Тип числа має відповідати домену задачі.
33. Що таке escape-символи у рядках Python і як їх використовують? Наведіть приклади для `\n`, `\t`, `\r`, `\"`, і `\'`.

Python

Escape-послідовності це спеціальні комбінації з \, які кодують службові символи всередині рядка.

  • \n — новий рядок
  • \t — табуляція
  • \r — повернення каретки
  • \" — подвійна лапка в рядку з "
  • \' — одинарна лапка в рядку з '
text = "A\tB\n\"quoted\"\nI\'m here\rX"

Коротко:

  • Escape-символи керують форматуванням рядка.
  • Дозволяють вставляти лапки без ламання синтаксису.
  • Для шляхів/regex часто зручно використовувати raw-рядки r"...".
34. Поясніть використання методів `strip`, `lstrip` та `rstrip` у Python. Чим вони відрізняються?

Python

Методи працюють з краями рядка:

  • strip() — прибирає символи з обох боків;
  • lstrip() — лише зліва;
  • rstrip() — лише справа.

За замовчуванням видаляють whitespace, або конкретний набір символів:

"  hello  ".strip()      # "hello"
"--id--".strip("-")      # "id"

Коротко:

  • strip-сімейство не змінює рядок in-place, а повертає новий.
  • Різниця лише у стороні очищення.
  • Часто використовується на вхідних даних.
35. Опишіть призначення методу `count` у рядках. Як він працює?

Python

str.count(sub[, start[, end]]) рахує кількість неперекривних входжень підрядка sub у вибраному діапазоні.

"banana".count("an")      # 2
"banana".count("a", 2)    # 2

Повертає 0, якщо входжень немає.

Коротко:

  • count швидко дає кількість входжень підрядка.
  • Підтримує обмеження пошуку через start/end.
  • Рахує неперекривні входження.
36. Як працює метод `join` у Python і для чого найчастіше його використовують?

Python

sep.join(iterable) з'єднує послідовність рядків в один рядок через розділювач sep.

names = ["Ada", "Linus", "Guido"]
result = ", ".join(names)  # "Ada, Linus, Guido"

Типове застосування: формування CSV-подібних рядків, логів, SQL-фрагментів, URL-шляхів, повідомлень.

Коротко:

  • join це стандартний і швидкий спосіб склеювання рядків.
  • Викликається на розділювачі, не на списку.
  • Ефективніший за багаторазове += у циклі.
37. Що таке slicing (нарізка) у Python і як з її допомогою отримати підрядки? Наведіть приклади з позитивними та негативними індексами.

Python

Slicing це вибір частини послідовності через [start:stop:step].

s = "python"
s[1:4]     # "yth"
s[:2]      # "py"
s[-3:]     # "hon"
s[::-1]    # "nohtyp"

start включається, stop не включається.

Коротко:

  • Slicing працює для рядків, списків, кортежів.
  • Негативні індекси рахуються з кінця.
  • Це базовий інструмент обробки послідовностей без циклів.
38. Поясніть метод `replace` для рядків. Як ним можна замінити декілька входжень підрядка?

Python

str.replace(old, new, count=-1) повертає новий рядок, де old замінено на new. За замовчуванням замінюються всі входження.

"foo bar foo".replace("foo", "baz")      # "baz bar baz"
"foo bar foo".replace("foo", "baz", 1)   # "baz bar foo"

Коротко:

  • replace не змінює рядок in-place.
  • Без count замінює всі входження.
  • count дозволяє контролювати кількість замін.
39. Як працює метод `split` у рядках?

Python

split(sep=None, maxsplit=-1) ділить рядок на список підрядків.

  • без sep ділить по будь-яких пробільних символах;
  • з sep ділить по конкретному розділювачу;
  • maxsplit обмежує кількість розділень.
"a,b,c".split(",")         # ["a", "b", "c"]
"a b   c".split()          # ["a", "b", "c"]
"k=v=x".split("=", 1)      # ["k", "v=x"]

Коротко:

  • split перетворює рядок у список токенів.
  • sep=None має спеціальну поведінку для whitespace.
  • maxsplit корисний для парсингу key/value форматів.
40. Як працює приведення типів (type casting)?

Python

Type casting це явне перетворення значення між типами через конструктори: int(), float(), str(), bool(), list(), tuple(), set(), dict().

age = int("42")
price = float("19.99")
text = str(10)

Некоректні дані спричиняють виняток (ValueError, TypeError), тому для зовнішнього вводу потрібна валідація.

Коротко:

  • Python надає явні функції конверсії типів.
  • Не всі значення можна безпечно конвертувати.
  • Для user input потрібні перевірки й обробка винятків.
41. Як Python автоматично конвертує різні типи даних у значення boolean?

Python

У булевому контексті Python застосовує truthiness правила.

False-подібні значення:

  • False, None;
  • нульові числа: 0, 0.0, 0j;
  • порожні колекції: "", [], {}, set(), tuple(), range(0).

Усе інше зазвичай True.

bool([])      # False
bool("0")     # True

Коротко:

  • Boolean-конверсія базується на truthy/falsy правилах.
  • Порожнє і нульове -> False.
  • Непорожній рядок "0" все одно True.
42. Поясніть, як перетворити рядок у ціле або дійсне число у Python. Що буде, якщо рядок не можна перетворити у число?

Python

Для перетворення використовуйте int() і float():

count = int("42")
ratio = float("3.14")

Якщо рядок невалідний, Python піднімає ValueError.

def parse_int(value: str) -> int | None:
    try:
        return int(value)
    except ValueError:
        return None

Коротко:

  • int()/float() виконують явну конверсію з рядка.
  • Невалідний формат -> ValueError.
  • Для зовнішніх даних потрібен try/except.
43. Що робить Python при порівнянні різних типів даних, таких як цілі числа і дійсні, або цілі числа і boolean?

Python

Python дозволяє числове порівняння сумісних numeric-типів.

  • int і float порівнюються за значенням.
  • bool є підкласом int: False == 0, True == 1.
1 == 1.0      # True
True == 1     # True
False < 1     # True

Для несумісних типів (наприклад int і str) порівняння порядку (<, >) викликає TypeError.

Коротко:

  • int, float, bool мають спільну numeric-семантику.
  • bool поводиться як 0/1 у порівняннях.
  • Несумісні типи не порівнюються оператором порядку.
44. Як Python обробляє порівняння між списками та set?

Python

list і set це різні типи з різною семантикою, тому пряме порівняння порядку між ними не підтримується.

[1, 2] == {1, 2}   # False
[1, 2] < {1, 2}    # TypeError

Якщо треба порівняти склад елементів, нормалізуйте тип:

set([1, 2]) == {1, 2}  # True

Коротко:

  • list і set порівнюються як різні структури даних.
  • == між ними зазвичай False.
  • Для змістовного порівняння спершу приводьте до одного типу.
45. Опишіть використання функції `bool()` у Python.

Python

bool(x) повертає булеве значення x за правилами truthiness.

Типові сценарії:

  • явне перетворення значення до True/False;
  • читабельні умови;
  • побудова фільтрів.
bool("text")   # True
bool("")       # False
bool(0)        # False

Коротко:

  • bool() уніфікує перевірку "порожнє/непорожнє".
  • Спирається на вбудовані правила truthy/falsy.
  • Корисний для валідації та умовної логіки.
46. У чому різниця між `list` і `tuple`?

Python

Головна різниця: list mutable, tuple immutable.

Практичні наслідки:

  • list підходить для даних, що змінюються;
  • tuple підходить для фіксованих записів;
  • tuple можна використовувати як ключ словника (якщо елементи хешовані).
coords: tuple[float, float] = (50.45, 30.52)
queue: list[str] = ["task-1", "task-2"]

Коротко:

  • list для змінних колекцій.
  • tuple для незмінних структур даних.
  • Вибір впливає на безпечність API і використання в dict/set.
47. Як можна створити `tuple`?

Python

Основні способи:

t1 = (1, 2, 3)
t2 = 1, 2, 3
t3 = tuple([1, 2, 3])
single = (42,)     # важлива кома
empty = ()

Для одного елемента кома обов'язкова, інакше це просто вираз у дужках.

Коротко:

  • tuple створюють через літерал або tuple(iterable).
  • single = (x,) це коректний одноелементний tuple.
  • Порожній tuple: ().
48. Яка різниця між методом `reverse` і нарізкою `[::-1]` при перевертанні списку?

Python

list.reverse() змінює існуючий список in-place і повертає None.

lst[::-1] створює новий перевернутий список (копію).

values = [1, 2, 3]
values.reverse()      # values -> [3, 2, 1]

other = values[::-1]  # новий список

Коротко:

  • reverse() змінює оригінал.
  • [::-1] повертає новий об'єкт.
  • Вибір залежить від потреби зберегти вихідні дані.
49. Поясніть призначення та використання методу `sort` для списків. Як відсортувати список у спадному порядку?

Python

list.sort() сортує список in-place. Підтримує параметри:

  • key — функція ключа сортування;
  • reverse=True — спадний порядок.
nums = [5, 1, 7]
nums.sort(reverse=True)  # [7, 5, 1]

Якщо потрібна нова відсортована копія, використовуйте sorted(iterable).

Коротко:

  • sort() змінює список на місці.
  • Для спадання використовуйте reverse=True.
  • sorted() зручний, коли оригінал треба зберегти.
50. У чому різниця між методом `copy` і прямим присвоєнням одного списку іншому? Чому іноді важливо використовувати `copy`?

Python

Присвоєння копіює лише посилання:

a = [1, 2]
b = a
b.append(3)   # a теж зміниться

a.copy() створює новий (поверхневий) список:

a = [1, 2]
b = a.copy()
b.append(3)   # a не зміниться

Для вкладених структур може знадобитися copy.deepcopy.

Коротко:

  • = не копіює дані, а ділить один об'єкт між змінними.
  • copy() ізолює зміни на рівні верхнього списку.
  • Для вкладених об'єктів використовуйте deepcopy.
51. Що робить метод `pop` у списку? Чим його поведінка відрізняється якщо вказати індекс і якщо ні?

Python

pop() видаляє і повертає елемент зі списку.

  • pop() без аргументів видаляє останній елемент;
  • pop(i) видаляє елемент за індексом i.
items = ["a", "b", "c"]
last = items.pop()     # "c"
first = items.pop(0)   # "a"

Некоректний індекс викликає IndexError.

Коротко:

  • pop поєднує видалення і повернення значення.
  • Без індексу працює з кінцем списку.
  • З індексом видаляє конкретну позицію.
52. Як об'єднати два списки у Python використовуючи оператор `+` і метод `extend`? У чому ключова різниця між цими двома способами?

Python

a + b створює новий список, а a.extend(b) змінює список a in-place.

a = [1, 2]
b = [3, 4]

c = a + b         # [1, 2, 3, 4], a не змінився
a.extend(b)       # a -> [1, 2, 3, 4]

Коротко:

  • + повертає новий об'єкт.
  • extend() мутує існуючий список.
  • Вибір залежить від того, чи треба зберегти оригінал.
53. Для чого застосовуються функції `min`, `max`, `sum`, `all`, `any` у контексті списків?

Python

Це базові агрегатори для ітерованих колекцій:

  • min() / max() — мінімум і максимум;
  • sum() — сума чисел;
  • all()True, якщо всі елементи truthy;
  • any()True, якщо хоча б один елемент truthy.
nums = [3, 10, 1]
min(nums), max(nums), sum(nums)  # (1, 10, 14)
all([True, 1, "x"])              # True
any([0, "", None, 5])            # True

Коротко:

  • Функції зменшують boilerplate у циклах.
  • Працюють з будь-яким iterable.
  • Добре поєднуються з генераторами для lazy-обробки.
54. Що таке словник (`dict`) у Python і чим він відрізняється від інших структур даних?

Python

dict це асоціативний масив: зберігає пари ключ -> значення. Ключі мають бути hashable, значення можуть бути будь-якого типу.

Відмінності:

  • доступ за ключем, а не за індексом;
  • середня складність пошуку/вставки/видалення близька до O(1);
  • з Python 3.7+ зберігає порядок вставки ключів.

Коротко:

  • dict оптимальний для швидкого доступу за ключем.
  • Це основна структура для конфігурацій і мапінгів.
  • Ключі повинні бути незмінними (hashable).
55. У чому різниця між отриманням значення зі словника через квадратні дужки `[]` і методом `get`?

Python

d[key] повертає значення або кидає KeyError, якщо ключа немає.

d.get(key, default) повертає значення або default (або None), без винятку.

config = {"timeout": 30}
config["timeout"]         # 30
config.get("retries", 3)  # 3

Коротко:

  • [] для обов'язкових ключів.
  • get() для опціональних полів.
  • get() зменшує кількість try/except у читанні даних.
56. Опишіть, як можна оновити та видалити елементи зі словника.

Python

Оновлення:

  • d[key] = value — вставити/оновити один ключ;
  • d.update({...}) — масове оновлення.

Видалення:

  • del d[key] — видалити ключ (помилка, якщо нема);
  • d.pop(key, default) — видалити і повернути значення;
  • d.popitem() — видалити останню пару;
  • d.clear() — очистити весь словник.

Коротко:

  • Для upsert підходять [] і update().
  • Для безпечного видалення частіше використовують pop().
  • Обирайте API залежно від бажаної поведінки при відсутньому ключі.
57. Для чого призначені методи `keys`, `values` та `items` у словниках?

Python

Ці методи повертають view-об'єкти словника:

  • keys() — всі ключі;
  • values() — всі значення;
  • items() — пари (key, value).
data = {"a": 1, "b": 2}
for key, value in data.items():
    ...

View динамічні: відображають актуальний стан словника.

Коротко:

  • keys/values/items потрібні для ітерації та перевірок.
  • items() найзручніший для циклу з ключем і значенням.
  • Це не копії, а "живі" представлення даних.
58. Як влаштований `dict` у Python під капотом?

Python

dict реалізований як hash table з оптимізаціями пам'яті і швидкого доступу. Ключ хешується, за хешем обирається комірка, колізії розв'язуються внутрішнім алгоритмом probing.

Практичні наслідки:

  • середній доступ близько O(1);
  • якість __hash__ і __eq__ впливає на поведінку;
  • mutable об'єкти не можна використовувати як ключі.

Коротко:

  • dict це високопродуктивна hash-структура.
  • Швидкість досягається за рахунок хешування.
  • Ключі мають бути hashable та стабільними.
59. Що таке hash function?

Python

Hash function перетворює об'єкт у ціле число (hash), яке використовується для швидкого розміщення/пошуку в dict і set.

Умови для коректності:

  • якщо a == b, тоді hash(a) == hash(b);
  • значення хеша має бути стабільним протягом життя об'єкта.

Коротко:

  • Хеш-функція є основою швидкої роботи dict/set.
  • Вона не гарантує унікальність (можливі колізії).
  • Для custom-класів важлива узгодженість __eq__ і __hash__.
60. Що таке `set` у Python і чим він відрізняється від інших структур даних?

Python

set це невпорядкована колекція унікальних елементів.

Відмінності:

  • автоматично прибирає дублікати;
  • швидкі операції перевірки належності (x in s);
  • підтримує теоретико-множинні операції: union/intersection/difference.

Коротко:

  • set оптимальний для унікальності та membership-check.
  • Порядок елементів не гарантується.
  • Елементи мають бути hashable.
61. Як створити `set` у Python і які обмеження існують щодо елементів set?

Python

Створення:

s1 = {1, 2, 3}
s2 = set([1, 2, 2, 3])   # {1, 2, 3}
empty = set()            # не {}

Обмеження:

  • елементи мають бути hashable (наприклад int, str, tuple);
  • list, dict, set не можна додати напряму.

Коротко:

  • set() створює порожню множину.
  • Дублікати автоматично видаляються.
  • Дозволені лише hashable-елементи.
62. Для чого використовується метод `intersection()` у Python set і чим він відрізняється від `union()`?

Python

intersection() повертає спільні елементи множин, а union() об'єднує всі унікальні елементи з обох множин.

a = {1, 2, 3}
b = {3, 4}

a.intersection(b)  # {3}
a.union(b)         # {1, 2, 3, 4}

Коротко:

  • intersection = перетин (спільне).
  • union = об'єднання (все унікальне).
  • Обидва методи базові для порівняння наборів даних.
63. Які типи даних є immutable?

Python

Поширені immutable-типи:

  • int, float, bool, complex;
  • str, bytes;
  • tuple (якщо елементи теж immutable);
  • frozenset;
  • NoneType.

Коротко:

  • Immutable-об'єкт не можна змінити після створення.
  • Замість мутації створюється новий об'єкт.
  • Такі типи безпечніші для ключів dict/елементів set.
64. Які типи даних є mutable?

Python

Поширені mutable-типи:

  • list;
  • dict;
  • set;
  • bytearray;
  • більшість user-defined об'єктів класів.

Коротко:

  • Mutable-об'єкти змінюються in-place.
  • Мутації можуть створювати побічні ефекти через спільні посилання.
  • Потрібно обережно працювати з копіюванням.
65. Чому важливо розуміти змінюваність, працюючи зі словниками або set?

Python

dict і set базуються на хешуванні, тому їх елементи/ключі мають бути стабільними (hashable). Mutable-об'єкти не можна безпечно використовувати як ключі або елементи set.

Також мутація значень через спільні reference часто породжує неочікувані баги.

Коротко:

  • Mutability впливає на коректність ключів у dict/set.
  • Спільні mutable-об'єкти часто дають побічні ефекти.
  • Явні копії і контроль мутацій зменшують баги.
66. Що таке `None`?

Python

None це спеціальне singleton-значення типу NoneType, яке означає "відсутність значення".

Типові сценарії:

  • функція нічого явно не повертає;
  • опціональні параметри;
  • маркер "дані ще не задані".

Перевірка виконується через is:

if value is None:
    ...

Коротко:

  • None означає відсутність значення.
  • Це singleton, тому порівнюють через is.
  • Часто використовується в API як опціональний стан.
67. Що таке `id` у Python, як його використовувати і чому це важливо?

Python

id(obj) повертає ідентифікатор об'єкта (унікальний в межах життєвого циклу об'єкта в поточному процесі).

Корисно для діагностики:

  • чи це той самий об'єкт;
  • чи створено копію;
  • чи сталася мутація спільного reference.

Коротко:

  • id допомагає аналізувати identity об'єктів.
  • Корисний у дебазі копіювання/мутацій.
  • Не використовується як бізнес-ідентифікатор.
68. Яка різниця між операторами `is` та `==`?

Python

== порівнює значення (еквівалентність), а is порівнює ідентичність (чи це один і той самий об'єкт у пам'яті).

a = [1, 2]
b = [1, 2]
a == b   # True
a is b   # False

Для None завжди використовуйте is.

Коротко:

  • == про рівність значень.
  • is про той самий об'єкт.
  • value is None це правильний стиль перевірки.
69. Як застосовується Singleton pattern у Python? Наведіть приклади Singleton-об'єктів у Python.

Python

Singleton означає один глобальний екземпляр об'єкта в процесі. У Python його часто замінюють модульним рівнем (модулі імпортуються один раз).

Приклади singleton-об'єктів мови:

  • None;
  • True і False;
  • Ellipsis.

У прикладному коді замість жорсткого Singleton частіше використовують DI-контейнер або фабрики для кращої тестованості.

Коротко:

  • Python вже має вбудовані singleton-об'єкти.
  • Часто достатньо модульного скоупу без окремого патерна.
  • Зловживання Singleton погіршує тестованість.
70. Для чого використовуються оператори `break` та `continue` в циклах Python?

Python

break достроково завершує цикл, continue пропускає поточну ітерацію і переходить до наступної.

for n in range(10):
    if n == 5:
        break
    if n % 2 == 0:
        continue

Коротко:

  • break зупиняє цикл повністю.
  • continue пропускає лише поточний крок.
  • Вони роблять керування циклом явним і читабельним.
71. Поясніть поняття нескінченного циклу. У яких випадках доречно використовувати нескінченний цикл і як забезпечити його коректне завершення?

Python

Нескінченний цикл це цикл без природної умови завершення, наприклад while True. Використовується для daemon/worker-процесів, polling, event-loop логіки.

Коректне завершення:

  • чітка умова break;
  • обробка сигналів завершення;
  • таймаути й try/finally для clean-up.
while True:
    task = queue.get()
    if task is None:
        break
    handle(task)

Коротко:

  • while True доречний для довгоживучих сервісних циклів.
  • Потрібен контрольований механізм зупинки.
  • Завжди передбачайте вивільнення ресурсів.
72. Опишіть, як працюють вкладені цикли у Python. Які проблеми з продуктивністю можуть виникати при їх використанні і як їх уникати?

Python

Вкладений цикл це цикл усередині іншого циклу. Часто складність стає O(n*m) або гірше, що критично на великих даних.

Оптимізації:

  • замінювати пошук у списках на set/dict;
  • виносити інваріанти за межі внутрішнього циклу;
  • застосовувати генератори, itertools, векторизацію;
  • профілювати "гарячі" ділянки.

Коротко:

  • Вкладені цикли швидко множать вартість обчислень.
  • Структури даних часто важливіші за мікрооптимізації.
  • Профілювання показує, що саме треба оптимізувати.
73. Що таке structural pattern matching (`match`/`case`)?

Python

match/case (Python 3.10+) це механізм розбору структури даних за шаблонами. Працює з літералами, типами, послідовностями, словниками та класами.

def handle(message: dict[str, object]) -> str:
    match message:
        case {"type": "ping"}:
            return "pong"
        case {"type": "user", "id": int(user_id)}:
            return f"user:{user_id}"
        case _:
            return "unknown"

Коротко:

  • match/case читається краще для складного розгалуження.
  • Дозволяє одночасно перевіряти форму і виймати дані.
  • Особливо корисний для протоколів/подій і парсингу структур.
74. У яких випадках pattern matching кращий за `if`/`elif`?

Python

match/case кращий, коли потрібно:

  • перевіряти багато взаємовиключних форм даних;
  • розпаковувати вкладені структури;
  • уникати довгих ланцюгів if/elif.

if/elif кращий для простих булевих умов і короткої логіки.

Коротко:

  • Для структурних сценаріїв краще match/case.
  • Для простих умов достатньо if/elif.
  • Критерій вибору: читабельність і підтримка.
75. Що таке функція в Python?

Python

Функція це іменований callable-блок коду, який приймає аргументи, повертає результат і дозволяє інкапсулювати логіку.

def normalize_name(name: str) -> str:
    return name.strip().title()

Функції підтримують default-значення, keyword-аргументи, *args/**kwargs, анотації типів, декоратори.

Коротко:

  • Функція це базова одиниця повторного використання коду.
  • Вона формує чіткий контракт через параметри й return.
  • Type hints роблять контракт явним.
76. Які існують типи аргументів функцій?

Python

У сучасному Python:

  • positional-only (/);
  • positional-or-keyword;
  • keyword-only (*);
  • variadic positional (*args);
  • variadic keyword (**kwargs).
def f(a, /, b, *, c, **kwargs):
    ...

Коротко:

  • Python дає гнучкий контроль способу виклику функції.
  • / і * формалізують API-контракт.
  • *args/**kwargs корисні для розширюваних інтерфейсів.
77. Що таке позиційні та іменовані аргументи?

Python

Позиційні аргументи передаються за порядком, іменовані (keyword) за назвою параметра.

def connect(host: str, port: int) -> str:
    return f"{host}:{port}"

connect("localhost", 5432)          # позиційно
connect(host="localhost", port=5432) # іменовано

Коротко:

  • Позиційні залежать від порядку параметрів.
  • Іменовані підвищують читабельність виклику.
  • Їх можна комбінувати з дотриманням правил сигнатури.
78. Що таке аргументи за замовчуванням і які з ними можуть бути проблеми?

Python

Default-аргументи підставляються, якщо значення не передано під час виклику. Вони обчислюються один раз при визначенні функції.

Проблема: mutable default.

def add_item(item: int, bucket: list[int] | None = None) -> list[int]:
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket

Коротко:

  • Defaults зручні для стабільних значень.
  • Mutable default може накопичувати стан між викликами.
  • Безпечний патерн: None + ініціалізація всередині.
79. Поясніть призначення та використання `*args` і `**kwargs` у функціях Python. Чим вони відрізняються?

Python

*args збирає додаткові позиційні аргументи в tuple. **kwargs збирає додаткові іменовані аргументи в dict.

def log_event(event: str, *args: object, **kwargs: object) -> None:
    ...

Використання: обгортки, адаптери API, декоратори, проксування параметрів.

Коротко:

  • *args = додаткові позиційні.
  • **kwargs = додаткові іменовані.
  • Вони роблять функції гнучкими, але потребують чіткої валідації.
80. Як визначити функцію з type annotations у Python? Наведіть приклад і поясніть переваги цього підходу.

Python

Типи задаються в сигнатурі параметрів і return-значення.

def process_users(users: list[dict[str, object]]) -> list[str]:
    return [str(user["name"]) for user in users if bool(user.get("active"))]

Переваги:

  • кращий DX (autocomplete, навігація);
  • статична перевірка в CI;
  • явний контракт для інших розробників.

Коротко:

  • Анотації типів документують API.
  • Вони зменшують ризик помилок інтеграції.
  • Найбільша користь у середніх і великих кодових базах.
81. Що таке lambda-функції?

Python

lambda це анонімна одно-виразна функція. Зазвичай використовується для коротких callbacks у sorted, map, filter.

users = [{"name": "Ada"}, {"name": "Bob"}]
users_sorted = sorted(users, key=lambda u: u["name"])

Для складної логіки краще звичайна def-функція.

Коротко:

  • lambda зручна для коротких локальних виразів.
  • Обмежена одним виразом.
  • Для читабельності складний код краще виносити в def.
82. Яка область видимості змінних у функції?

Python

Python використовує правило LEGB для пошуку імен:

  • Local;
  • Enclosing (зовнішні функції);
  • Global (модуль);
  • Builtins.

Усередині функції присвоєння створює локальну змінну, якщо не оголошено global або nonlocal.

Коротко:

  • Scope визначає, де ім'я доступне і змінюване.
  • LEGB пояснює порядок пошуку змінних.
  • Неправильне розуміння scope часто дає UnboundLocalError.
83. Що таке локальні та глобальні змінні у Python?

Python

Локальні змінні живуть у тілі функції. Глобальні змінні визначені на рівні модуля.

Для зміни глобальної з функції потрібен global, але це зазвичай варто уникати через неявні залежності.

Коротко:

  • Локальні безпечніші для підтримки й тестування.
  • Глобальні спрощують доступ, але ускладнюють контроль стану.
  • Краще передавати залежності параметрами.
84. Яка різниця між локальними і nonlocal-змінними у Python?

Python

nonlocal використовується у вкладеній функції для зміни змінної із найближчого enclosing-scope (не глобального).

def counter() -> callable:
    value = 0

    def inc() -> int:
        nonlocal value
        value += 1
        return value

    return inc

Коротко:

  • Локальна змінна належить поточній функції.
  • nonlocal змінює стан зовнішньої функції.
  • Це ключовий механізм для closure зі станом.
85. Що таке unpacking у Python і як його застосовують при присвоєнні?

Python

Unpacking це розкладання елементів послідовності/структури в окремі змінні.

x, y = (10, 20)
first, *middle, last = [1, 2, 3, 4]

Працює також зі словниками в match/case, викликах функцій і циклах.

Коротко:

  • Unpacking робить код компактнішим і читабельнішим.
  • Підтримує "зірочковий" збір решти значень.
  • Часто використовується в парсингу структур даних.
86. Поясніть поняття packing значень у Python і наведіть приклади.

Python

Packing це збирання кількох значень в одну структуру (tuple, list, dict).

point = 10, 20                 # tuple packing

def collect(*args: int) -> tuple[int, ...]:
    return args

*args і **kwargs це типовий приклад packing аргументів.

Коротко:

  • Packing збирає багато значень в один контейнер.
  • Найчастіше використовується в сигнатурах функцій.
  • Добре поєднується з unpacking на стороні виклику.
87. Для чого використовують оператор `*` при packing та unpacking?

Python

* у параметрах функції пакує позиційні аргументи (*args), а у виклику розпаковує iterable в позиційні аргументи.

def add(a: int, b: int) -> int:
    return a + b

nums = [2, 3]
add(*nums)  # 5

Також * використовується у присвоєнні для захоплення "решти" елементів.

Коротко:

  • * універсальний оператор для роботи з varargs.
  • У сигнатурі пакує, у виклику розпаковує.
  • Зменшує шаблонний код при передачі даних.
88. Що таке closure і яке його відношення до decorator-ів?

Python

Closure це внутрішня функція, яка "пам'ятає" змінні enclosing-scope навіть після завершення зовнішньої функції.

Decorator зазвичай реалізують саме через closure: обгортка зберігає посилання на оригінальну функцію і додаткові параметри.

Коротко:

  • Closure це функція + захоплений контекст.
  • Decorator часто є практичним застосуванням closure.
  • Дає можливість додати поведінку без зміни тіла функції.
89. Що таке decorator у Python і як він працює?

Python

Decorator це callable, який приймає функцію/клас і повертає модифіковану версію (обгортку).

from functools import wraps

def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

Застосування: логування, кешування, авторизація, ретраї, метрики.

Коротко:

  • Decorator додає cross-cutting поведінку.
  • Працює через обгортання callable.
  • Дозволяє не дублювати технічну логіку в бізнес-коді.
90. Чи можна використовувати декілька decorator-ів для однієї функції?

Python

Так, можна стекувати кілька декораторів. Вони застосовуються знизу вгору (ближчий до def виконується першим обгортанням).

@decorator_a
@decorator_b
def handler() -> None:
    ...

Еквівалент: handler = decorator_a(decorator_b(handler)).

Коротко:

  • Кілька декораторів допустимі і поширені.
  • Порядок застосування має значення.
  • Стек декораторів треба документувати для прозорості.
91. Опишіть можливу проблему з черговістю decorator-ів при їх застосуванні до функції.

Python

Неправильний порядок може змінити семантику: наприклад, кешування до перевірки доступу може закешувати небажаний результат або обійти очікувану логіку.

Типові ризики:

  • логування бачить вже змінені аргументи;
  • ретраї обгортають не той виняток;
  • кеш ставиться до/після валідації в неправильній точці.

Коротко:

  • Порядок декораторів впливає на поведінку функції.
  • Це часта причина прихованих багів.
  • Для критичних ланцюжків потрібні тести на порядок виконання.
92. Чи можна створити decorator за допомогою класу?

Python

Так. Клас-декоратор реалізує __call__, щоб екземпляр поводився як функція.

class CallCounter:
    def __init__(self, func):
        self.func = func
        self.calls = 0

    def __call__(self, *args, **kwargs):
        self.calls += 1
        return self.func(*args, **kwargs)

Коротко:

  • Decorator можна реалізувати не лише функцією, а й класом.
  • Клас зручний, коли потрібен внутрішній стан.
  • Ключовий механізм: __call__.
93. Як визначити decorator, що приймає параметри?

Python

Потрібна трирівнева структура: фабрика декоратора -> декоратор -> wrapper.

from functools import wraps

def retry(times: int):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times - 1):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    pass
            return func(*args, **kwargs)
        return wrapper
    return decorator

Коротко:

  • Параметризований декоратор це функція, що повертає декоратор.
  • Часто використовують closure для збереження параметрів.
  • Такий підхід зручний для конфігурованої поведінки.
94. Для чого використовують `functools.wraps` у decorator-функціях?

Python

functools.wraps копіює метадані оригінальної функції в wrapper: ім'я, docstring, модуль, а також __wrapped__.

Це важливо для:

  • коректного дебагу й логів;
  • інтроспекції та документації;
  • сумісності з інструментами, що читають сигнатуру.

Коротко:

  • wraps зберігає "ідентичність" оригінальної функції.
  • Без нього декоровані функції втрачають корисні метадані.
  • Це best practice для всіх wrapper-декораторів.
95. Як працюють dict comprehension, list comprehension і set comprehension?

Python

Comprehension створює колекцію з виразу і циклу(ів), опційно з фільтром.

squares = [x * x for x in range(6)]
mapping = {x: x * x for x in range(6)}
unique = {x % 3 for x in range(10)}

Формат:

  • list: [expr for x in it if cond]
  • set: {expr for x in it if cond}
  • dict: {k_expr: v_expr for x in it if cond}

Коротко:

  • Comprehension компактно виражає трансформацію даних.
  • Працює для list, set, dict.
  • Дає чистіший код, ніж ручне додавання в циклі.
96. Які переваги використання list comprehensions у порівнянні зі звичайними циклами?

Python

Переваги:

  • коротший і декларативніший код;
  • менше службових змінних;
  • зазвичай трохи краща продуктивність у CPython;
  • нижчий ризик забути append.

Недолік: для дуже складної логіки comprehension погіршує читабельність.

Коротко:

  • List comprehension добре підходить для простих трансформацій.
  • Часто швидший і чистіший за ручний цикл.
  • Для складних гілок краще звичайний for.
97. Чи можете навести приклад вкладеної list comprehension?

Python

Приклад flatten матриці:

matrix = [[1, 2], [3, 4], [5, 6]]
flat = [item for row in matrix for item in row]  # [1, 2, 3, 4, 5, 6]

Приклад з умовою:

pairs = [(x, y) for x in range(3) for y in range(3) if x != y]

Коротко:

  • Вкладена comprehension це кілька for в одному виразі.
  • Порядок for відповідає вкладеним циклам.
  • Використовуйте, коли вираз залишається читабельним.
98. Що швидше у Python: list comprehension або створення списку за допомогою циклу?

Python

У типових сценаріях list comprehension трохи швидший за цикл з append, бо має оптимізований байткод і менше накладних операцій.

Важливо: реальний приріст залежить від тіла операції, тому для критичних ділянок варто вимірювати (timeit, pyperf).

Коротко:

  • Часто швидше: list comprehension.
  • Різниця може бути невеликою.
  • Для production-рішень орієнтуйтесь на вимірювання.
99. Що таке iterator у Python?

Python

Iterator це об'єкт, який повертає елементи послідовно і пам'ятає поточний стан. Він реалізує протокол:

  • __iter__() повертає себе;
  • __next__() повертає наступний елемент або кидає StopIteration.

Коротко:

  • Iterator дає поелементний доступ без повного завантаження даних.
  • Це база для for, генераторів і lazy-обробки.
  • Після вичерпання iterator не "перемотується" сам.
100. Як створити iterator з ітерованого об'єкта за допомогою функції `iter()`?

Python

Викликайте iter(iterable), щоб отримати iterator.

items = [10, 20, 30]
it = iter(items)

Після цього значення читаються через next(it).

Коротко:

  • iter() перетворює iterable на iterator.
  • Це явний спосіб керувати ітерацією вручну.
  • Використовується у низькорівневій обробці потоку даних.
101. Для чого призначена функція `next()` при роботі з iterator-ами?

Python

next(iterator[, default]) повертає наступний елемент iterator. Коли елементи закінчились, кидає StopIteration або повертає default, якщо його передано.

it = iter([1, 2])
next(it)        # 1
next(it)        # 2
next(it, None)  # None

Коротко:

  • next() дає ручний контроль кроку ітерації.
  • Без default кінець потоку -> StopIteration.
  • З default зручно безпечно читати потік.
102. Чи можна взаємозамінно використовувати методи `__next__()` і `__iter__()` з функціями `next()` та `iter()`?

Python

Так, але з нюансом протоколу:

  • iter(obj) викликає obj.__iter__();
  • next(it) викликає it.__next__().

Тобто функції є стандартним інтерфейсом до dunder-методів і зазвичай використовуються замість прямого виклику.

Коротко:

  • iter() відповідає __iter__().
  • next() відповідає __next__().
  • У прикладному коді краще викликати вбудовані функції.
103. Яка роль `StopIteration` у проєктуванні iterator-ів і коли це зазвичай виникає?

Python

StopIteration сигналізує, що iterator вичерпано. for-цикл перехоплює його автоматично і завершує ітерацію.

Зазвичай виникає:

  • при виклику next(it) після останнього елемента;
  • у custom-ітераторах, коли дані закінчились.

Коротко:

  • StopIteration це штатний кінець потоку.
  • Його не треба логувати як "помилку" в normal-flow.
  • У власних iterator-ах його треба піднімати коректно.
104. Що таке generator і чим він відрізняється від iterator чи звичайної функції?

Python

Generator це спеціальний iterator, який створюється функцією з yield. Він генерує значення по одному і зберігає внутрішній стан між викликами.

Відмінності:

  • від звичайної функції: не завершується одним return, а "пауза/продовження";
  • від ручного iterator: простіша реалізація без явного класу __next__.

Коротко:

  • Generator це найзручніший спосіб lazy-ітерації.
  • Дає менше коду, ніж custom iterator-клас.
  • Особливо корисний для великих потоків даних.
105. Як створити generator-функцію?

Python

Потрібно визначити функцію з yield.

def countdown(start: int):
    current = start
    while current > 0:
        yield current
        current -= 1

Виклик countdown(3) повертає generator-об'єкт, який можна ітерувати.

Коротко:

  • Наявність yield робить функцію генератором.
  • Generator повертає значення поетапно.
  • Стан функції зберігається між ітераціями.
106. Як ключове слово `yield` забезпечує функціональність generator-ів і чому вони економлять пам'ять?

Python

yield повертає чергове значення і "заморожує" контекст функції (локальні змінні, позицію виконання). Наступний next() відновлює виконання з цієї точки.

Економія пам'яті: дані не створюються повністю наперед, а обчислюються on-demand.

Коротко:

  • yield реалізує паузу/продовження виконання.
  • Generator підтримує lazy evaluation.
  • Це знижує memory footprint для великих наборів даних.
107. У чому різниця між `return` і `yield`?

Python

return завершує функцію і повертає одне фінальне значення. yield повертає проміжне значення і зберігає стан для подальшого продовження.

У генераторі return означає кінець ітерації (StopIteration).

Коротко:

  • return -> завершення функції.
  • yield -> поетапна видача значень.
  • yield використовується для потокової обробки.
108. Що таке Object-Oriented Programming (OOP) та його головні принципи у Python?

Python

OOP це підхід, де дані і поведінка об'єднані в об'єктах.

Ключові принципи:

  • інкапсуляція;
  • наслідування;
  • поліморфізм;
  • абстракція.

У Python OOP зазвичай поєднується з композицією і duck typing.

Коротко:

  • OOP структурує домен через класи й об'єкти.
  • Python підтримує OOP гнучко, без надмірного шаблонного коду.
  • На практиці композиція часто краща за глибоке наслідування.
109. Що таке `class` у Python?

Python

class це шаблон (blueprint) для створення об'єктів з атрибутами та методами.

class User:
    def __init__(self, name: str) -> None:
        self.name = name

Клас задає структуру і поведінку майбутніх екземплярів.

Коротко:

  • Клас описує дані й операції над ними.
  • На основі класу створюються об'єкти (instances).
  • Це базова одиниця моделювання в OOP.
110. Як створити object у Python?

Python

Об'єкт створюється викликом класу:

class User:
    def __init__(self, name: str) -> None:
        self.name = name

user = User("Ada")

Під час створення виконується __init__ для ініціалізації стану.

Коротко:

  • Object = екземпляр класу.
  • Створення: instance = ClassName(...).
  • __init__ налаштовує початкові атрибути.
111. Що таке об'єкти та атрибути?

Python

Об'єкт це конкретний екземпляр типу/класу. Атрибут це пов'язане з об'єктом ім'я-значення (дані або метод).

user.name       # атрибут-дані
user.save()     # атрибут-метод

Коротко:

  • Об'єкт зберігає стан і поведінку.
  • Атрибути описують цей стан/поведінку.
  • Доступ до атрибутів: крапкова нотація.
112. Яку роль виконує метод `__init__()` у класі?

Python

__init__ це ініціалізатор екземпляра: викликається після створення об'єкта і заповнює початковий стан.

class Account:
    def __init__(self, owner: str, balance: float = 0.0) -> None:
        self.owner = owner
        self.balance = balance

Коротко:

  • __init__ задає стартові атрибути об'єкта.
  • Працює як вхідна точка конфігурації instance.
  • Не створює об'єкт, а лише ініціалізує його.
113. Для чого параметр `self` у методах класу Python?

Python

self це посилання на поточний екземпляр, через нього метод читає/змінює інстанс-атрибути.

def deposit(self, amount: float) -> None:
    self.balance += amount

Ім'я self не зарезервоване синтаксично, але є загальноприйнятим стандартом.

Коротко:

  • self зв'язує метод з конкретним об'єктом.
  • Через self доступні інстанс-дані.
  • Це обов'язковий перший параметр instance method.
114. Як визначати методи у класі?

Python

Методи визначаються як функції всередині class.

class Calculator:
    def add(self, a: int, b: int) -> int:
        return a + b

Типові види: instance (self), class (@classmethod, cls), static (@staticmethod, без self/cls).

Коротко:

  • Метод це функція в тілі класу.
  • Тип методу визначається декоратором і сигнатурою.
  • Найчастіше використовується instance method.
115. Поясніть концепцію "все є об'єктом" у Python і наведіть приклади.

Python

У Python майже все є об'єктом: числа, рядки, функції, класи, модулі. Тому у всього є тип, атрибути й поведінка.

type(10)          # <class 'int'>
type(len)         # <class 'builtin_function_or_method'>
type(str)         # <class 'type'>

Коротко:

  • Єдина об'єктна модель спрощує мову.
  • Функції й класи теж об'єкти першого класу.
  • Це робить Python гнучким для метапрограмування.
116. Наведіть приклад класу з методами, які виконують обчислення на основі атрибутів.

Python

class Rectangle:
    def __init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

    def perimeter(self) -> float:
        return 2 * (self.width + self.height)

Коротко:

  • Методи можуть обчислювати значення з інстанс-атрибутів.
  • Це інкапсулює доменну логіку в об'єкті.
  • API класу стає самодокументованим.
117. Опишіть ситуацію, коли може знадобитися використання кількох класів у Python-програмі.

Python

Коли домен має кілька відповідальностей, їх розділяють по класах. Наприклад e-commerce: Order, OrderItem, PaymentService, InventoryService.

Це дає:

  • чіткий розподіл відповідальностей;
  • слабше зв'язування;
  • простішу заміну/тестування компонентів.

Коротко:

  • Кілька класів потрібні для моделювання складного домену.
  • Розподіл відповідальностей підвищує підтримуваність.
  • Композиція класів зазвичай краща за "god object".
118. У чому різниця між instance method, class method і static method?

Python

  • Instance method: має self, працює з конкретним екземпляром.
  • Class method: має cls, працює з класом загалом.
  • Static method: не має self/cls, це утилітарна функція в namespace класу.

Коротко:

  • Instance -> логіка екземпляра.
  • Class -> логіка класу/альтернативні конструктори.
  • Static -> допоміжна логіка без доступу до стану.
119. Що таке `@classmethod` у Python і чим він відрізняється від звичайних методів?

Python

@classmethod передає першим аргументом клас (cls), а не екземпляр. Часто використовується для factory-методів.

class User:
    def __init__(self, name: str) -> None:
        self.name = name

    @classmethod
    def from_email(cls, email: str) -> "User":
        return cls(email.split("@")[0])

Коротко:

  • classmethod працює на рівні класу.
  • Зручний для альтернативних конструкторів.
  • Не потребує вже створеного instance.
120. Що таке `@staticmethod` у класах Python і коли його доцільно застосовувати?

Python

@staticmethod визначає метод без автоматичного self або cls. Він логічно належить класу, але не залежить від стану.

class Math:
    @staticmethod
    def clamp(value: int, min_v: int, max_v: int) -> int:
        return max(min_v, min(value, max_v))

Коротко:

  • staticmethod це функція в namespace класу.
  • Використовуйте для допоміжної логіки без доступу до атрибутів.
  • Не підходить, якщо потрібен стан instance або class.
121. У чому різниця між `@classmethod` і `@staticmethod` у класах Python?

Python

Різниця у першому аргументі і рівні доступу:

  • @classmethod отримує cls і може працювати з класовим станом;
  • @staticmethod не отримує нічого автоматично.

classmethod частіше для фабрик/поліморфних конструкторів, staticmethod для утиліт.

Коротко:

  • classmethod знає про клас.
  • staticmethod ізольований від класового/інстанс стану.
  • Вибір залежить від потреби доступу до cls.
122. Що таке інстанс-атрибути у класах Python і чим вони відрізняються від класових атрибутів?

Python

Інстанс-атрибути належать конкретному об'єкту (self.x). Класові атрибути належать класу і спільні для всіх екземплярів.

class User:
    role = "member"           # class attribute
    def __init__(self, name: str) -> None:
        self.name = name      # instance attribute

Коротко:

  • Інстанс-атрибути зберігають індивідуальний стан.
  • Класові атрибути зберігають спільну конфігурацію.
  • Мутації class-атрибутів впливають на всі instance.
123. Що таке `__slots__` у Python?

Python

__slots__ обмежує набір дозволених атрибутів у класі і може зменшити споживання пам'яті, прибираючи __dict__ для екземплярів.

class Point:
    __slots__ = ("x", "y")
    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y

Коротко:

  • __slots__ корисний для великої кількості легковагових об'єктів.
  • Дає memory-оптимізацію і контроль атрибутів.
  • Зменшує гнучкість динамічного додавання полів.
124. Що таке magic methods (dunder методи) у класах Python і чому їх називають "магічними"?

Python

Dunder-методи (__init__, __str__, __len__, __eq__, ...) це спеціальні хуки, які Python викликає автоматично у відповідь на оператори та built-ins.

Їх називають "магічними", бо вони інтегрують ваш клас у поведінку мови.

Коротко:

  • Dunder-методи визначають протокольну поведінку об'єкта.
  • Вони дозволяють вашим класам поводитись "як вбудовані типи".
  • Використовуйте їх лише коли є чітка семантична потреба.
125. Для чого призначений метод `__del__`?

Python

__del__ це фіналізатор, який може викликатися перед знищенням об'єкта GC. Його поведінка не детермінована, тому для керування ресурсами краще використовувати context managers (with) і явне закриття.

Коротко:

  • __del__ не гарантує своєчасного виконання.
  • Не покладайте критичний clean-up лише на нього.
  • Рекомендований підхід: with/try-finally.
126. Наведіть приклад використання магічного методу `__str__` для визначення текстового представлення власного класу у Python.

Python

class User:
    def __init__(self, name: str, active: bool) -> None:
        self.name = name
        self.active = active

    def __str__(self) -> str:
        status = "active" if self.active else "inactive"
        return f"User(name={self.name}, status={status})"

str(user) і print(user) використають __str__.

Коротко:

  • __str__ дає людинозрозуміле представлення об'єкта.
  • Корисний для логів і CLI-виводу.
  • Має бути коротким і читабельним.
127. Для чого використовуються `__str__` і `__repr__`?

Python

__str__ орієнтований на кінцевого читача. __repr__ орієнтований на розробника/debug, бажано щоб був однозначним.

print(obj) переважно використовує __str__, а REPL/repr(obj) використовує __repr__.

Коротко:

  • __str__ для дружнього виводу.
  • __repr__ для технічного представлення.
  • Добра практика: мати обидва, якщо клас доменний.
128. Як працює operator overloading у Python і чому це корисно?

Python

Operator overloading це реалізація dunder-методів операторів (__add__, __sub__, __eq__, ...), щоб об'єкти власного класу підтримували оператори.

class Vector2:
    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y

    def __add__(self, other: "Vector2") -> "Vector2":
        return Vector2(self.x + other.x, self.y + other.y)

Коротко:

  • Дає природний синтаксис для доменних типів.
  • Покращує виразність API.
  • Важливо зберігати математично очікувану семантику.
129. Що таке inheritance (наслідування)?

Python

Наслідування дозволяє створити дочірній клас, який успадковує атрибути й методи базового класу та може розширювати/перевизначати поведінку.

Коротко:

  • Наслідування сприяє повторному використанню коду.
  • Дочірній клас може перевизначати методи базового.
  • Надмірна ієрархія ускладнює підтримку, тому часто краще композиція.
130. Що таке single inheritance у Python?

Python

Single inheritance означає, що клас має лише одного прямого батька.

class Animal:
    ...

class Dog(Animal):
    ...

Це найпростіша і зазвичай найчитабельніша форма наслідування.

Коротко:

  • Один дочірній клас -> один базовий клас.
  • Проста модель резолюції методів.
  • Часто достатня для більшості бізнес-моделей.
131. Як реалізувати наслідування у класах Python і який для цього використовується синтаксис?

Python

Синтаксис: class Child(Base):.

class Base:
    def greet(self) -> str:
        return "hello"

class Child(Base):
    def greet(self) -> str:
        return "hi"

За потреби виклику базової логіки використовуйте super().

Коротко:

  • Наслідування задається у дужках після імені класу.
  • Дочірній клас отримує API базового.
  • Перевизначення дозволяє адаптувати поведінку.
132. Як отримати доступ до членів базового класу у дочірньому класі?

Python

Доступ можливий напряму через успадковані атрибути/методи або через super().

class Base:
    def greet(self) -> str:
        return "hello"

class Child(Base):
    def greet(self) -> str:
        return super().greet() + " world"

Коротко:

  • Успадковані члени доступні в дочірньому класі автоматично.
  • super() коректно викликає логіку базового класу.
  • Це важливо для розширення, а не дублювання поведінки.
133. Для чого функція `super()` у наслідуванні Python, як її використовують?

Python

super() повертає проксі до наступного класу за MRO, щоб викликати його методи. Типово використовується в __init__ і при cooperative multiple inheritance.

class Child(Base):
    def __init__(self, value: int) -> None:
        super().__init__()
        self.value = value

Коротко:

  • super() викликає батьківську реалізацію без жорсткого імені класу.
  • Допомагає підтримувати MRO-коректність.
  • Рекомендований спосіб роботи з наслідуванням.
134. Опишіть функцію `isinstance()` у Python і наведіть приклад її використання.

Python

isinstance(obj, cls_or_tuple) перевіряє, чи об'єкт належить типу або його підкласу.

isinstance(10, int)                 # True
isinstance(True, int)               # True
isinstance("x", (str, bytes))       # True

Коротко:

  • isinstance безпечніший за type(obj) is ... у поліморфному коді.
  • Враховує ієрархію наслідування.
  • Підтримує кортеж типів.
135. Поясніть функцію `issubclass()` у Python і наведіть приклад її застосування.

Python

issubclass(Sub, Base) перевіряє, чи клас Sub є підкласом Base.

class Animal: ...
class Dog(Animal): ...

issubclass(Dog, Animal)  # True

Коротко:

  • Працює з класами, не з екземплярами.
  • Зручно для валідації API на рівні типів.
  • Враховує транзитивне наслідування.
136. Що таке multiple inheritance (множинне наслідування)?

Python

Multiple inheritance це спадкування одного класу від кількох базових класів.

class A: ...
class B: ...
class C(A, B): ...

Дає гнучкість, але вимагає дисципліни у дизайні методів і super().

Коротко:

  • Один клас може успадковувати поведінку з кількох джерел.
  • Корисно для mixin-підходу.
  • Може ускладнювати розуміння MRO.
137. Як працює MRO (Method Resolution Order) у multiple inheritance?

Python

MRO визначає порядок пошуку методів у ієрархії класів. У Python використовується алгоритм C3 linearization.

Переглянути порядок можна через ClassName.__mro__ або ClassName.mro().

Коротко:

  • MRO вирішує, з якого базового класу брати метод.
  • Порядок передбачуваний і формально визначений (C3).
  • Для cooperative inheritance всі класи мають викликати super().
138. Які переваги та недоліки використання multiple inheritance?

Python

Переваги:

  • повторне використання поведінки з кількох джерел;
  • зручні mixins для "додавання" можливостей.

Недоліки:

  • складніший MRO;
  • ризик конфліктів імен/поведінки;
  • складніший дебаг і онбординг.

Коротко:

  • MI потужний, але вимагає строгих правил дизайну.
  • Для більшості кейсів композиція простіша.
  • Використовуйте MI переважно для невеликих mixins.
139. Що таке Mixins?

Python

Mixin це невеликий клас з вузькою додатковою поведінкою, який призначений для комбінування через наслідування, а не для самостійного використання.

Приклади: TimestampMixin, JsonSerializableMixin.

Коротко:

  • Mixin додає одну конкретну можливість.
  • Зазвичай не має власного повного життєвого циклу.
  • Добре поєднується з multiple inheritance.
140. Що таке encapsulation (інкапсуляція) у Python?

Python

Інкапсуляція це приховування внутрішньої реалізації за стабільним публічним API. У Python це реалізується переважно конвенціями і property, а не жорсткими модифікаторами доступу.

Коротко:

  • Клієнт коду працює з інтерфейсом, а не з деталями.
  • Інкапсуляція зменшує зв'язування між компонентами.
  • Полегшує зміну внутрішньої реалізації без ломання API.
141. Яка різниця між доступом public, private і protected?

Python

У Python це здебільшого naming-conventions:

  • public: name — доступний усюди;
  • protected: _name — внутрішнє використання за домовленістю;
  • private: __name — name mangling (_ClassName__name), не абсолютний захист.

Коротко:

  • Python не має строгих access modifiers як у Java/C#.
  • _name і __name це сигнали наміру для розробників.
  • Реальний контроль доступу будується через API-дизайн.
142. Що таке polymorphism (поліморфізм) і як він реалізується у Python?

Python

Поліморфізм це можливість працювати з різними об'єктами через спільний інтерфейс. У Python часто реалізується duck typing і протоколами.

def render(obj) -> str:
    return obj.to_text()

Будь-який об'єкт з методом to_text підходить.

Коротко:

  • Один інтерфейс, багато реалізацій.
  • У Python поліморфізм часто "поведінковий", а не ієрархічний.
  • Це спрощує розширення системи новими типами.
143. Що таке abstraction (абстракція) у Python?

Python

Абстракція це виділення суттєвого API і приховування зайвих деталей реалізації. Клієнт працює з контрактом, а не з внутрішніми кроками.

Коротко:

  • Абстракція знижує когнітивне навантаження.
  • Полегшує заміну реалізації без змін клієнтського коду.
  • Реалізується через інтерфейси, ABC, протоколи, фасади.
144. Як реалізувати data abstraction?

Python

Підхід:

  • сховати прямий доступ до внутрішніх полів (_field);
  • надати контрольований API через методи/@property;
  • валідувати інваріанти в setter-логіці.
class Temperature:
    def __init__(self, celsius: float) -> None:
        self.celsius = celsius

    @property
    def celsius(self) -> float:
        return self._celsius

    @celsius.setter
    def celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError("invalid temperature")
        self._celsius = value

Коротко:

  • Data abstraction захищає інваріанти об'єкта.
  • property дає контрольований доступ до стану.
  • Внутрішні деталі можна змінювати без зміни API.
145. Що таке ABC та `@abstractmethod`?

Python

ABC (Abstract Base Class) це абстрактний базовий клас з модуля abc. @abstractmethod позначає метод, який обов'язково має реалізувати підклас.

from abc import ABC, abstractmethod

class Storage(ABC):
    @abstractmethod
    def save(self, data: bytes) -> None:
        ...

Коротко:

  • ABC формалізує контракт ієрархії.
  • @abstractmethod забороняє інстанціювати неповну реалізацію.
  • Корисно для плагінних/розширюваних архітектур.
146. Що таке property у Python і як їх застосовують?

Python

property перетворює методи доступу в атрибутоподібний API з можливістю валідації, обчислень або lazy-логіки.

class User:
    def __init__(self, name: str) -> None:
        self._name = name

    @property
    def name(self) -> str:
        return self._name

Коротко:

  • property дає контроль доступу без зміни зовнішнього синтаксису.
  • Підходить для валідації й derived-значень.
  • Дозволяє еволюціонувати API без ломання клієнтів.
147. Що таке `@property`?

Python

@property це декоратор для getter-методу. Разом з @x.setter і @x.deleter формує керований атрибут.

Коротко:

  • @property дозволяє читати метод як поле.
  • Допомагає інкапсулювати внутрішню реалізацію.
  • Часто використовується для backward-compatible API.
148. Що таке descriptor у Python?

Python

Descriptor це об'єкт, який реалізує __get__, __set__ або __delete__ і керує доступом до атрибутів іншого класу.

Через descriptor працюють property, classmethod, staticmethod.

Коротко:

  • Descriptor це низькорівневий механізм атрибутного доступу.
  • Дозволяє повторно використовувати логіку валідації/проксування полів.
  • Це основа багатьох метапрограмних технік Python.
149. Чим відрізняються property і descriptor?

Python

property це готовий високорівневий descriptor для одного атрибута. Кастомний descriptor це загальніший механізм, який можна перевикористати в багатьох полях/класах.

Коротко:

  • property простіший і локальний.
  • Descriptor гнучкіший і масштабованіший.
  • property фактично побудований на descriptor-протоколі.
150. Коли доцільніше використовувати property, а коли descriptor?

Python

Використовуйте property, коли логіка стосується одного-двох полів конкретного класу. Використовуйте descriptor, коли ту саму логіку (валідація, кастинг, ленива ініціалізація) треба повторно застосувати в багатьох класах.

Коротко:

  • Локальна логіка поля -> property.
  • Повторне використання політики доступу -> descriptor.
  • Descriptor вигідний у великих доменних моделях.
151. Для чого використовують `setattr()`, `getattr()` і `hasattr()`? Яка між ними різниця?

Python

Це функції динамічного доступу до атрибутів:

  • getattr(obj, name[, default]) — прочитати атрибут;
  • setattr(obj, name, value) — встановити атрибут;
  • hasattr(obj, name) — перевірити наявність.
value = getattr(user, "email", None)
if not hasattr(user, "active"):
    setattr(user, "active", True)

Коротко:

  • getattr/setattr/hasattr потрібні для динамічної роботи з об'єктами.
  • Корисні в generic-коді, serialization, адаптерах.
  • Важливо не зловживати, щоб не втратити читабельність.
152. Поясніть значення методу `__set_name__` у Python-descriptor-ах та наведіть приклад його застосування.

Python

__set_name__(self, owner, name) викликається під час створення класу і дає descriptor-у знати ім'я атрибута, до якого його прив'язано.

class Field:
    def __set_name__(self, owner, name): self.name = name

Коротко:

  • __set_name__ ініціалізує descriptor контекстом класу.
  • Дозволяє робити перевикористовувані валідатори полів.
  • Спрацьовує один раз на етапі class creation.
153. Що таке `dataclass` і коли його варто використовувати?

Python

@dataclass автоматично генерує boilerplate (__init__, __repr__, __eq__). Підходить для моделей даних без складної поведінки.

from dataclasses import dataclass
@dataclass(slots=True)
class User: name: str; active: bool = True

Коротко:

  • dataclass скорочує код для data-моделей.
  • Добрий вибір для DTO/конфігурацій.
  • Для складної валідації часто потрібні інші інструменти.
154. У чому різниця між `dataclass` і Pydantic?

Python

dataclass фокусується на зручному описі структури. Pydantic додає runtime-валідацію, парсинг і серіалізацію даних.

Коротко:

  • dataclass легший і швидший для внутрішніх моделей.
  • Pydantic кращий для зовнішнього вводу/API.
  • Вибір залежить від потреби валідації під час виконання.
155. Що таке type hints і навіщо вони потрібні?

Python

Type hints це анотації типів у сигнатурах і змінних, що формують явний контракт. Вони покращують IDE-підказки і статичний аналіз.

Коротко:

  • Type hints підвищують зрозумілість API.
  • Дають раннє виявлення класу помилок.
  • Корисні навіть у динамічній мові.
156. Як працює статична перевірка типів (mypy)?

Python

mypy читає type hints і аналізує код без запуску, перевіряючи сумісність типів. Він ловить помилки інтеграції до runtime.

Коротко:

  • mypy виконує compile-time-подібну перевірку для Python.
  • Працює найкраще в CI.
  • Строгі режими зменшують кількість production-багів.
157. Як забезпечити type safety у Python-проєкті?

Python

  • послідовно додати type hints у публічний API;
  • увімкнути mypy/pyright у CI;
  • використовувати TypedDict, Protocol, generics;
  • мінімізувати Any і неявні касти.

Коротко:

  • Type safety це процес, а не разова дія.
  • Найбільший ефект дає CI-гейт на типи.
  • Поступове підвищення strictness працює краще за "big bang".
158. Що таке `TypedDict`?

Python

TypedDict описує типізовану форму словника: які ключі очікуються і яких типів їх значення.

from typing import TypedDict
class UserPayload(TypedDict): name: str; active: bool

Коротко:

  • TypedDict типізує dict з фіксованими ключами.
  • Зручний для JSON-подібних структур.
  • Перевіряється статичним аналізатором.
159. Що таке `Protocol` у typing?

Python

Protocol описує поведінковий контракт (structural typing): тип сумісний, якщо має потрібні методи/атрибути, незалежно від наслідування.

Коротко:

  • Protocol реалізує duck typing у статичній типізації.
  • Зменшує жорстке зв'язування з конкретними класами.
  • Корисний для тестованих і розширюваних API.
160. Що таке Generics у Python?

Python

Generics дозволяють писати типи й функції, параметризовані іншими типами. У сучасному синтаксисі: class Box[T]: ..., def first[T](...) -> T.

Коротко:

  • Generics роблять типи повторно використовуваними.
  • Підсилюють type safety колекцій і контейнерів.
  • Зменшують дублювання типізованого коду.
161. Що таке Pydantic і для чого він використовується?

Python

Pydantic це бібліотека для опису схем даних з runtime-валідацією, перетворенням типів і зручною серіалізацією.

Типові кейси: FastAPI request/response моделі, конфігурація застосунку.

Коротко:

  • Pydantic валідовує зовнішні дані під час виконання.
  • Зручний для API і інтеграцій.
  • Дає чіткі помилки валідації та схеми.
162. Що таке exception-и у Python?

Python

Exception це об'єкт, який сигналізує про помилкову або виняткову ситуацію під час виконання коду.

Коротко:

  • Exception-и переривають normal-flow.
  • Їх треба обробляти там, де можна прийняти рішення.
  • Коректна модель помилок підвищує надійність системи.
163. Які є три типи помилок у Python і чим вони відрізняються?

Python

  • Syntax errors: помилки синтаксису до запуску;
  • Runtime exceptions: помилки під час виконання;
  • Logical errors: код виконується, але результат неправильний.

Коротко:

  • Syntax/runtime ловить інтерпретатор.
  • Логічні помилки ловляться тестами і рев'ю.
  • Кожен тип потребує різного підходу діагностики.
164. Як використовувати `try`, `except`, `else` та `finally`?

Python

try містить ризикову операцію, except обробляє помилку, else виконується коли помилки не було, finally виконується завжди (clean-up).

try: data = load()
except FileNotFoundError: data = {}
else: validate(data)
finally: close_connections()

Коротко:

  • else тримайте для коду "успішного шляху".
  • finally для ресурсів/очищення.
  • Ловіть конкретні exception-и, не "все підряд".
165. Що означає порядок `except`-категорій?

Python

except перевіряються зверху вниз, тому спочатку ставлять найконкретніші винятки, а загальні (Exception) в кінці.

Коротко:

  • Порядок except впливає на те, який обробник спрацює.
  • Широкий except зверху "з'їдає" специфічні кейси.
  • Це критично для правильного recovery-flow.
166. Яке призначення ключового слова `assert` у Python-коді?

Python

assert перевіряє інваріант і піднімає AssertionError, якщо умова хибна. Підходить для внутрішніх перевірок розробника, не для валідації user input.

Коротко:

  • assert документує "це має бути істинним".
  • Не замінює продакшен-обробку помилок.
  • Використовуйте для контрактів у внутрішній логіці.
167. Чим `raise` відрізняється від простого print error message у Python?

Python

raise змінює контроль потоку і сигналізує помилку викликувачеві. print лише виводить текст і не зупиняє помилковий сценарій.

Коротко:

  • raise це механізм обробки помилок, print ні.
  • Exception-и можна централізовано ловити/логувати.
  • print для діагностики, не для error-contract.
168. Як створити власний виняток (custom exception)?

Python

Створіть клас, що наслідує Exception (або конкретніший базовий виняток).

class InvalidOrderError(Exception):
    pass

Коротко:

  • Custom exception робить помилки доменно-виразними.
  • Дозволяє точково ловити потрібні кейси.
  • Краще за універсальний ValueError скрізь.
169. Яке значення має створення custom exceptions у Python і як вони покращують обробку помилок?

Python

Власні exception-и формують явну error-модель домену й відділяють технічні помилки від бізнес-правил.

Коротко:

  • Покращують читабельність і підтримку обробників.
  • Дають точнішу семантику в логах/API.
  • Спрощують тестування негативних сценаріїв.
170. Як організовані exception-и у Python і яка ієрархія exception-класів?

Python

Ієрархія побудована від BaseException. Для прикладного коду майже завжди працюють з нащадками Exception.

Ключові гілки: ValueError, TypeError, KeyError, OSError, RuntimeError.

Коротко:

  • Exception-и утворюють дерево класів.
  • Краще ловити конкретні підкласи.
  • BaseException зазвичай не перехоплюють у бізнес-логіці.
171. Які підходи до обробки кількох різних exception-ів у Python і чому їх слід обробляти окремо?

Python

Підходи:

  • окремі except для кожного типу;
  • групування логічно однакових: except (A, B):;
  • окремі recovery-стратегії для кожної категорії.

Коротко:

  • Різні exception-и часто потребують різних дій.
  • Окрема обробка зменшує приховані дефекти.
  • Логи стають точнішими й кориснішими.
172. Для чого потрібно повторно піднімати (re-raise) exception у Python і коли це буває корисно?

Python

Re-raise (raise без аргументів у except) дозволяє додати контекст (лог, метрики, cleanup) і передати ту саму помилку вище.

Коротко:

  • Re-raise зберігає початковий traceback.
  • Корисний для централізованої обробки на вищому рівні.
  • Не "ковтайте" критичні помилки без потреби.
173. Чому використання try-except блоку варто обмежувати у програмах Python і як це впливає на продуктивність?

Python

try/except потрібен, але не має обгортати великі блоки "про всяк випадок". Особливо дорого, коли винятки виникають часто (exception-driven flow).

Коротко:

  • Ловіть лише очікувані помилки в вузькому місці.
  • Часті exception-и погіршують продуктивність і читабельність.
  • Prefer explicit checks там, де це доречно.
174. Як обробляти помилки під час роботи з файлами?

Python

Використовуйте with і ловіть конкретні винятки (FileNotFoundError, PermissionError, UnicodeDecodeError, OSError).

try:
    with open(path, "r", encoding="utf-8") as f:
        content = f.read()
except FileNotFoundError:
    ...

Коротко:

  • with гарантує закриття файлу.
  • Обробляйте конкретні I/O помилки.
  • Логуйте контекст (шлях, режим, кодування).
175. Які режими роботи з файлами у Python?

Python

Основні режими:

  • r читання;
  • w перезапис;
  • a дозапис в кінець;
  • x створення нового файлу;
  • b бінарний режим;
  • t текстовий (за замовчуванням);
  • + читання+запис.

Коротко:

  • Режим визначає семантику безпеки й змін файлу.
  • w затирає вміст, a зберігає існуючий.
  • Для байтових даних використовуйте b.
176. Чому важливо закривати файли після операцій і що може статись, якщо залишити файл відкритим?

Python

Відкритий файл утримує дескриптор ОС. Якщо не закривати:

  • витік file descriptors;
  • блокування файлів/неповний flush;
  • нестабільність на довгих процесах.

Коротко:

  • Закриття файлу вивільняє ресурси ОС.
  • with автоматизує безпечне закриття.
  • Це критично для сервісів і batch-скриптів.
177. У чому різниця між методами `read()`, `readline()` та `readlines()` для читання файлів?

Python

  • read() читає весь файл (або n байт/символів);
  • readline() читає один рядок;
  • readlines() читає всі рядки у список.

Коротко:

  • read() і readlines() можуть бути важкими по пам'яті.
  • Для великих файлів краще ітерація for line in file.
  • Вибір залежить від обсягу і сценарію обробки.
178. Як у Python записати дані у файл і яка різниця між режимом `w` (write) та `a` (append)?

Python

Запис:

with open("out.txt", "w", encoding="utf-8") as f:
    f.write("hello\n")

w перезаписує файл з початку, a додає новий контент у кінець.

Коротко:

  • w затирає старі дані.
  • a зберігає існуючий вміст.
  • Для журналів зазвичай використовують a.
179. Як працювати з великими файлами ефективно?

Python

  • читати потоково (по рядках або чанках);
  • уникати завантаження всього файлу в пам'ять;
  • використовувати буферизацію і генератори;
  • для колонкових/табличних даних обирати відповідні формати й парсери.

Коротко:

  • Ключ: streaming замість full-read.
  • Генератори зменшують memory footprint.
  • Алгоритм обробки важливіший за мікрооптимізації.
180. Що таке контекстні менеджери?

Python

Контекстний менеджер керує ресурсом за протоколом __enter__/__exit__: відкриття/ініціалізація та гарантоване завершення/cleanup.

Коротко:

  • Дає безпечний lifecycle ресурсу.
  • Працює через with.
  • Зменшує кількість витоків ресурсів.
181. Як працює конструкція `with`?

Python

with викликає __enter__ при вході в блок і __exit__ при виході, навіть коли сталася помилка.

with open("data.txt", "r", encoding="utf-8") as f:
    data = f.read()

Коротко:

  • with гарантує cleanup.
  • Робить роботу з ресурсами декларативною.
  • Рекомендований для файлів, локів, транзакцій, сесій.
182. Як створити власний контекстний менеджер?

Python

Два підходи:

  • клас з __enter__/__exit__;
  • функція з contextlib.contextmanager.
from contextlib import contextmanager

@contextmanager
def temp_flag():
    yield

Коротко:

  • Клас підходить для складного стану.
  • contextmanager зручний для коротких сценаріїв.
  • Обидва дають гарантований cleanup.
183. Як Python керує пам'яттю?

Python

CPython використовує reference counting і циклічний garbage collector. Додатково є внутрішній алокатор (pymalloc) для дрібних об'єктів.

Коротко:

  • Базовий механізм: лічильник посилань.
  • GC прибирає циклічні посилання.
  • Пам'ять звільняється не завжди миттєво на рівні ОС.
184. Що таке reference counting у Python і чому воно важливе для керування пам'яттю?

Python

Кожен об'єкт має лічильник посилань. Коли він стає нулем, об'єкт можна звільнити. Це забезпечує швидке звільнення більшості короткоживучих об'єктів.

Коротко:

  • Reference counting дає передбачуваний lifecycle об'єктів.
  • Не вирішує циклічні посилання самостійно.
  • Разом з GC формує повну модель керування пам'яттю.
185. Що таке garbage collection у Python?

Python

Garbage collection у CPython знаходить і прибирає недосяжні цикли об'єктів, які не можуть бути очищені лише reference counting.

Коротко:

  • GC доповнює reference counting.
  • Особливо важливий для циклічних графів посилань.
  • Його можна контролювати через модуль gc.
186. Як отримати адресу пам'яті об'єкта у Python?

Python

У CPython id(obj) часто відповідає адресі об'єкта в пам'яті (як ціле число). Це діагностичний інструмент, а не стабільний зовнішній ідентифікатор.

Коротко:

  • id() дає identity об'єкта.
  • У CPython це зазвичай memory address.
  • Не використовуйте для бізнес-логіки.
187. Для чого функція `getrefcount()` з модуля `sys`, як вона працює у Python?

Python

sys.getrefcount(obj) повертає поточний лічильник посилань на об'єкт (у CPython). Значення зазвичай на 1 більше через тимчасове посилання аргументу.

Коротко:

  • Це інструмент діагностики memory-behavior.
  • Корисний для аналізу витоків посилань.
  • Не слід покладатися на нього в прикладній логіці.
188. Поясніть на прикладах різницю між mutable та immutable-об'єктами щодо reference counting і керування пам'яттю.

Python

Immutable-об'єкти не змінюються, тому "зміна" створює новий об'єкт. Mutable-об'єкти змінюються in-place, і всі посилання бачать зміну.

a = "x"; b = a; a += "y"   # новий об'єкт
x = [1]; y = x; y.append(2) # зміна того ж об'єкта

Коротко:

  • Immutable зменшують побічні ефекти.
  • Mutable ефективніші для in-place модифікацій.
  • Різниця критична для копіювання і спільних reference.
189. Яка різниця між shallow copy та deep copy у Python, коли використовувати кожен підхід?

Python

Shallow copy копіює лише зовнішній контейнер, вкладені об'єкти залишаються спільними. Deep copy рекурсивно копіює всю структуру.

import copy
copy.copy(obj)
copy.deepcopy(obj)

Коротко:

  • Shallow достатній для "плоских" структур.
  • Deep потрібен для ізольованої роботи з вкладеними mutable-даними.
  • Deep copy дорожчий по часу і пам'яті.
190. Що таке modules і packages у Python?

Python

Module це окремий .py-файл з кодом. Package це каталог модулів (простір імен), що організує код у більші блоки.

Коротко:

  • Module = одиниця коду.
  • Package = структура для масштабування модулів.
  • Розбиття на модулі/пакети покращує підтримуваність.
191. Як імпортувати module у Python?

Python

Основні форми:

  • import module;
  • import module as m;
  • from module import name;
  • from package import submodule.

Коротко:

  • Імпорт завантажує модуль і робить його доступним у namespace.
  • Alias (as) покращує читабельність і уникнення конфліктів.
  • Віддавайте перевагу явним імпортам.
192. Які бувають типи imports?

Python

Поширені типи:

  • абсолютні;
  • відносні;
  • вибіркові (from x import y);
  • alias-імпорти (as);
  • динамічні (importlib).

Коротко:

  • Тип імпорту впливає на читабельність і підтримку.
  • У продакшені переважно використовують абсолютні імпорти.
  • Динамічні імпорти застосовують точково.
193. Як працює система імпортів?

Python

Інтерпретатор шукає модуль по sys.path, завантажує його один раз і кешує в sys.modules. Повторний імпорт використовує кеш.

Коротко:

  • Імпорт = пошук + виконання модуля + кешування.
  • Це пояснює, чому модульний код виконується при першому імпорті.
  • Циклічні імпорти виникають через порядок ініціалізації модулів.
194. У чому різниця між абсолютним і відносним імпортом?

Python

Абсолютний імпорт починається від кореня пакета (from app.utils import x). Відносний імпорт використовує крапки (from .utils import x).

Коротко:

  • Абсолютні імпорти читабельніші і стабільніші.
  • Відносні зручні всередині пакета, але гірші для рефакторингу.
  • Для великих проєктів краще стандартно тримати абсолютні.
195. Що робить функція `dir()`, як її можна застосовувати до modules?

Python

dir(obj) повертає список доступних атрибутів/імен об'єкта. Для модулів це швидкий спосіб подивитися API.

import math
dir(math)

Коротко:

  • dir() допомагає в інтерактивному дослідженні модулів.
  • Корисний у REPL і дебазі.
  • Не замінює офіційну документацію.
196. Поясніть роль конструкції `if __name__ == "__main__":` у Python-скриптах.

Python

Цей блок виконується лише коли файл запущений як скрипт, а не імпортований як модуль. Це розділяє reusable-код і CLI-entrypoint.

Коротко:

  • Дозволяє модуль і запускати, і імпортувати.
  • Запобігає небажаному виконанню при імпорті.
  • Стандартний патерн для скриптів.
197. Що означає файл `__init__.py` у Python package?

Python

__init__.py позначає каталог як пакет і може ініціалізувати package-level API (наприклад, реекспортувати класи/функції).

Коротко:

  • Формує публічний інтерфейс пакета.
  • Може містити мінімальну ініціалізацію.
  • Не варто перевантажувати його важкою логікою.
198. Які best practices для стилю імпортів у Python-коді?

Python

  • групувати імпорти: stdlib, third-party, local;
  • уникати from x import *;
  • тримати імпорти на початку файлу (крім обґрунтованих lazy-import кейсів);
  • використовувати сортування/форматування (ruff, isort).

Коротко:

  • Консистентні імпорти покращують читабельність.
  • Явні імпорти зменшують конфлікти імен.
  • Автоматизація інструментами прибирає ручні помилки.
199. Які популярні built-in modules існують у Python?

Python

Приклади популярних модулів стандартної бібліотеки:

  • os, sys, pathlib, json, datetime, re,
  • collections, itertools, functools, typing,
  • asyncio, subprocess, logging, argparse.

Коротко:

  • Стандартна бібліотека покриває більшість базових задач.
  • Це знижує кількість зовнішніх залежностей.
  • Варто добре знати stdlib перед додаванням сторонніх пакетів.
200. Що ви знаєте про пакет `collections`, які ще built-in modules використовувались?

Python

collections надає високорівневі структури:

  • Counter, defaultdict, deque, namedtuple, ChainMap.

Типово комбінується з:

  • itertools для ітераційних пайплайнів;
  • functools для кешування/функц. інструментів;
  • pathlib, json, datetime в прикладних задачах.

Коротко:

  • collections розширює базові контейнери практичними структурами.
  • Часто підвищує простоту й продуктивність коду.
  • Добре працює в парі з itertools/functools.
201. Що повертає `sys.argv`?

Python

sys.argv повертає список аргументів командного рядка:

  • sys.argv[0] — ім'я скрипта;
  • решта елементів — передані аргументи.

Коротко:

  • sys.argv це базовий інтерфейс CLI-аргументів.
  • Значення приходять рядками.
  • Для складних CLI краще argparse.
202. Який основний модуль для роботи з операційною системою у Python?

Python

Основний модуль це os (разом з os.path), а для сучасного API шляхів рекомендовано pathlib.

Коротко:

  • os дає доступ до process/env/filesystem API ОС.
  • pathlib зручніший для роботи з шляхами.
  • У реальних проєктах часто використовують обидва.
203. Як перемішати елементи списку за допомогою модуля `random`?

Python

Використовуйте random.shuffle(list_) для in-place перемішування.

import random
items = [1, 2, 3, 4]
random.shuffle(items)

Коротко:

  • shuffle змінює оригінальний список.
  • Працює лише зі списками (mutable sequence).
  • Для нової копії: random.sample(items, k=len(items)).
204. Що таке virtual environment?

Python

Virtual environment це ізольоване середовище Python із власними пакетами й версіями залежностей для конкретного проєкту.

Коротко:

  • Ізоляція прибирає конфлікти залежностей між проєктами.
  • Стандартний інструмент: venv.
  • Це базова практика для reproducible development.
205. Як працює `pip`?

Python

pip встановлює, оновлює та видаляє пакети з індексів (здебільшого PyPI), розв'язує залежності і ставить їх у поточне середовище.

Коротко:

  • pip це стандартний пакетний менеджер Python.
  • Працює в межах активного venv/інтерпретатора.
  • Для стабільності важливі pinned-версії.
206. Що таке `requirements.txt`?

Python

requirements.txt це файл зі списком залежностей (часто з точними версіями), який використовується для відтворюваної інсталяції.

Коротко:

  • Формат простий і сумісний з pip install -r.
  • Найкраще фіксувати точні версії для production.
  • Часто генерується з lock-файлів.
207. Що таке `pyproject.toml` і чому він став стандартом?

Python

pyproject.toml це стандартизований файл конфігурації проєкту: metadata пакета, build-system, налаштування інструментів (ruff, pytest, mypy тощо).

Коротко:

  • Централізує конфігурацію Python-проєкту.
  • Підтримується сучасним packaging tooling.
  • Зменшує кількість розрізнених config-файлів.
208. Як керувати залежностями в сучасному Python-проєкті?

Python

  • ізолювати середовище (venv);
  • визначати залежності в pyproject.toml;
  • фіксувати lock/constraints для reproducibility;
  • регулярно оновлювати з CI-перевірками.

Коротко:

  • Керування залежностями має бути відтворюваним.
  • Не змішуйте глобальні і проєктні пакети.
  • Оновлення робіть контрольовано через тести.
209. Як правильно організувати структуру великого Python-проєкту?

Python

  • розділити код на доменні пакети;
  • мати окремі шари: api, services, domain, infrastructure;
  • виділити tests/, scripts/, configs/;
  • тримати явні межі модулів та публічний API.

Коротко:

  • Структура має відображати домен, а не випадкові технічні деталі.
  • Чіткі межі зменшують циклічні залежності.
  • Тести і tooling повинні бути first-class частиною дерева.
210. У чому різниця між автоматизованим і ручним тестуванням, які переваги автоматизованого тестування?

Python

Ручне тестування виконується людиною крок за кроком. Автоматизоване тестування виконується скриптами/фреймворками.

Коротко:

  • Автотести швидкі, повторювані і придатні для CI.
  • Ручне тестування корисне для exploratory UX-сценаріїв.
  • У продакшені потрібна комбінація обох підходів.
211. Що таке TDD (Test-Driven Development)?

Python

TDD це цикл: написати failing-тест -> мінімальна реалізація -> рефакторинг.

Коротко:

  • TDD формує API через тести.
  • Дає швидкий feedback про регресії.
  • Працює найкраще для модульної бізнес-логіки.
212. Які популярні фреймворки для тестування у Python?

Python

Найпопулярніші: pytest, unittest (stdlib), також hypothesis для property-based тестів.

Коротко:

  • pytest найчастіше вибирають для нових проєктів.
  • unittest корисний як стандартний базовий інструмент.
  • hypothesis підсилює покриття edge-cases.
213. Що таке `unittest` у Python?

Python

unittest це вбудований xUnit-фреймворк для тестів: test case класи, assert-методи, setup/teardown, test runner.

Коротко:

  • Частина стандартної бібліотеки.
  • Підходить для консервативних середовищ без зовнішніх залежностей.
  • Має більш "церемоніальний" синтаксис за pytest.
214. Що таке `pytest` і чим він відрізняється від `unittest` за синтаксисом і функціональністю?

Python

pytest це фреймворк з простим синтаксисом функційних тестів, потужними fixtures, параметризацією і екосистемою плагінів.

Коротко:

  • pytest менш шаблонний і зручніший для великих тест-сетів.
  • unittest class-based і стандартний у stdlib.
  • У сучасних проєктах переважає pytest.
215. Опишіть, як метод `pytest.raises` використовується для тестування виникнення конкретних exception-ів в Python-коді.

Python

pytest.raises(ExpectedError) перевіряє, що блок коду піднімає саме очікуваний виняток.

import pytest
with pytest.raises(ValueError):
    int("abc")

Коротко:

  • Тестує негативні сценарії явно.
  • Захищає від "мовчазного" проходження помилок.
  • Може перевіряти і повідомлення/атрибути винятку.
216. Що таке параметризація у тестах, як pytest підтримує її через `@pytest.mark.parametrize`?

Python

Параметризація запускає один тест на кількох наборах вхідних даних. У pytest це робиться декоратором @pytest.mark.parametrize.

Коротко:

  • Менше дублювання тест-коду.
  • Легко масштабувати кейси.
  • Краща прозорість покриття вхідних сценаріїв.
217. Як можна у pytest задати власні назви тестів при параметризованому тестуванні для кращої читабельності?

Python

Використовуйте параметр ids у parametrize.

@pytest.mark.parametrize("value,expected", [(2, True), (3, False)], ids=["even", "odd"])

Коротко:

  • ids робить output тест-рану зрозумілішим.
  • Полегшує діагностику падінь.
  • Особливо корисно при великій кількості кейсів.
218. Що таке Arrange/Setup у тестуванні, чому це важливо?

Python

Arrange/Setup це підготовка тестового стану: дані, об'єкти, моки, середовище. Якісний setup робить тест детермінованим.

Коротко:

  • Нестабільний setup = flaky-тести.
  • Добрий setup ізолює тест від зовнішніх ефектів.
  • Чіткий Arrange покращує читабельність сценарію.
219. Які етапи містить Cleanup/Teardown і чому це важливо?

Python

Teardown прибирає все, що створив тест: тимчасові файли, з'єднання, моки, тестові записи в БД.

Коротко:

  • Cleanup забезпечує ізоляцію між тестами.
  • Зменшує побічні ефекти і флейковість.
  • У pytest це зручно робити через fixture finalizers/yield-fixtures.
220. У чому різниця між методами `setUp()` та `setUpClass()` у unittest?

Python

setUp() виконується перед кожним тест-методом. setUpClass() (classmethod) виконується один раз перед усіма тестами класу.

Коротко:

  • setUp для пер-тестової ізоляції.
  • setUpClass для дорогих спільних ресурсів.
  • Надмірне спільне state через setUpClass може ускладнювати тести.
221. Що таке mock object і як він допомагає підвищити якість тестів?

Python

Mock object це підставний об'єкт, який імітує залежність і дозволяє контролювати поведінку/перевіряти виклики.

Коротко:

  • Моки ізолюють юніт від зовнішніх сервісів.
  • Роблять тести швидшими і стабільнішими.
  • Дають перевірку взаємодій, не лише результату.
222. Яка різниця між використанням `mock.patch` у unittest та `monkeypatch` у pytest для мокання об'єктів?

Python

mock.patch з unittest.mock патчить об'єкти за шляхом імпорту і має багату API для перевірки викликів. monkeypatch у pytest простіше змінює атрибути/env/dict на час тесту.

Коротко:

  • patch потужніший для mock-assertion сценаріїв.
  • monkeypatch зручний для швидких тестових підмін.
  • Часто їх комбінують залежно від кейсу.
223. Яке призначення параметра `scope` у pytest fixtures?

Python

scope визначає життєвий цикл fixture: function, class, module, package, session.

Коротко:

  • Менший scope = краща ізоляція.
  • Більший scope = швидший ран великих тест-сетів.
  • Вибір scope це баланс швидкості та незалежності.
224. Що таке складність алгоритму і як вона визначається?

Python

Складність алгоритму оцінює, як ростуть витрати часу/пам'яті зі збільшенням розміру вхідних даних n.

Коротко:

  • Вимірюють часову та просторову складність.
  • Оцінка зазвичай асимптотична.
  • Допомагає обирати алгоритм і структури даних.
225. Поясніть поняття big O-нотації і її значення в оцінці складності алгоритмів.

Python

Big-O описує верхню межу зростання вартості алгоритму при великому n, ігноруючи константи і нижчі члени.

Коротко:

  • Big-O показує масштабованість, а не точний час.
  • Дає мову для порівняння алгоритмів.
  • Критично важлива для продуктивності на великих обсягах.
226. Які види алгоритмічної складності найчастіше зустрічаються?

Python

Найчастіші: O(1), O(log n), O(n), O(n log n), O(n^2).

Коротко:

  • O(n) і O(n log n) найтиповіші в прикладних задачах.
  • O(n^2)+ часто стає вузьким місцем.
  • Потрібно враховувати також пам'ять, не лише час.
227. Наведіть приклади задач, які ефективно вирішуються лінійними (O(n)) алгоритмами.

Python

Приклади:

  • пошук максимуму/мінімуму;
  • фільтрація елементів;
  • підрахунок частот через Counter;
  • перевірка умов для кожного елемента.

Коротко:

  • O(n) означає один прохід по даних.
  • Це оптимально для багатьох агрегацій.
  • Лінійні алгоритми добре масштабуються.
228. Як працює `lru_cache` і коли його використовувати?

Python

functools.lru_cache кешує результати функції за аргументами і повертає готове значення при повторних викликах.

Коротко:

  • Ефективний для pure-функцій із повторюваними вхідними даними.
  • Не підходить для функцій із side effects.
  • maxsize контролює обсяг кешу в пам'яті.
229. Як профілювати продуктивність Python-коду?

Python

Базові інструменти:

  • timeit для мікровимірювань;
  • cProfile/pstats для call-level профілю;
  • py-spy/scalene для production-like аналізу.

Коротко:

  • Оптимізуйте лише після вимірювань.
  • Профілюйте реалістичні сценарії навантаження.
  • Фіксуйте baseline до/після змін.
230. Які основні способи оптимізації Python-коду?

Python

  • обрати правильні структури даних;
  • зменшити асимптотичну складність;
  • уникати зайвих копій;
  • використовувати генератори/lazy pipeline;
  • кешувати дорогі обчислення;
  • виносити hot paths у C/Rust/NumPy, якщо потрібно.

Коротко:

  • Найбільший приріст дає алгоритм, не синтаксичний твік.
  • Оптимізація має спиратися на профілювання.
  • Читабельність не варто жертвувати без виміряної вигоди.
231. Коли варто використовувати C extensions або PyPy?

Python

C extensions доречні для вузьких CPU-hotspots і інтеграції з нативними бібліотеками. PyPy доречний, коли довгоживучий pure-Python код виграє від JIT.

Коротко:

  • C extension: максимальна продуктивність ціною складності збірки.
  • PyPy: потенційний приріст без переписування на C.
  • Вибір робиться на основі бенчмарків вашого workload.
232. Що таке memory profiling?

Python

Memory profiling це вимірювання, де і скільки пам'яті споживає код у часі, щоб знайти витоки і важкі ділянки.

Коротко:

  • Показує memory hot spots і піки.
  • Корисний для batch/data workloads.
  • Інструменти: tracemalloc, memory_profiler, scalene.
233. Що таке GIL (Global Interpreter Lock)?

Python

GIL це механізм у CPython, який дозволяє лише одному thread одночасно виконувати Python bytecode в межах процесу.

Коротко:

  • GIL впливає на CPU-bound multithreading.
  • Для I/O-bound задач threads все ще корисні.
  • Для CPU-паралелізму частіше використовують multiprocessing.
234. Як глобальний інтерпретаторний лок Python (GIL) впливає на concurrency у CPython та які наслідки це має для multithreading?

Python

Через GIL потоки в CPython не виконують Python bytecode truly-parallel для CPU-bound коду. Вони кооперативно перемикаються.

Наслідки:

  • для I/O-bound потоків ефект добрий (очікування I/O перекривається);
  • для CPU-bound задач приріст від threads зазвичай обмежений.

Коротко:

  • GIL обмежує паралелізм потоків у CPU-bound сценаріях.
  • Threads залишаються корисними для мережі/диска.
  • Для CPU використовуйте процеси або нативні обчислення.
235. Як GIL впливає на продуктивність?

Python

GIL майже не заважає в I/O-bound задачах, але стримує throughput CPU-bound multithreaded Python-коду в одному процесі.

Коротко:

  • Вплив GIL залежить від типу навантаження.
  • CPU-bound + threads у CPython часто не масштабується.
  • Архітектуру слід обирати за профілем задач.
236. Поясніть концепцію threading у Python та як він відрізняється від multiprocessing.

Python

threading запускає кілька потоків в одному процесі зі спільною пам'яттю. multiprocessing запускає окремі процеси з окремою пам'яттю.

Коротко:

  • Threads легші і зручні для I/O-bound.
  • Processes дають реальний CPU-паралелізм.
  • Процеси мають вищі накладні витрати IPC/створення.
237. Коли використовувати multiprocessing замість threading?

Python

Коли задача CPU-bound і потребує використання кількох ядер у CPython. Приклади: heavy parsing, image/video processing, чисельні обчислення.

Коротко:

  • CPU-bound -> зазвичай multiprocessing.
  • I/O-bound -> зазвичай threading/asyncio.
  • Враховуйте вартість серіалізації між процесами.
238. Яка різниця між concurrency та parallelism у програмуванні, коли і яке з них доцільно використовувати?

Python

Concurrency це перекривання задач у часі. Parallelism це одночасне фізичне виконання задач на кількох ядрах.

Коротко:

  • Concurrency корисний для I/O затримок.
  • Parallelism потрібен для CPU-intensive обчислень.
  • У Python вибір інструмента залежить від типу bottleneck.
239. Що таке concurrency у Python?

Python

Concurrency у Python це організація виконання кількох задач так, щоб вони прогресували разом: через threads, asyncio або процеси.

Коротко:

  • Це про керування багатьма задачами, не обов'язково паралельно.
  • Дозволяє підвищити throughput I/O сценаріїв.
  • Потребує акуратного дизайну синхронізації та скасування.
240. У чому різниця між IO-bound і CPU-bound задачами?

Python

IO-bound задачі переважно чекають мережу/диск/БД. CPU-bound задачі переважно витрачають час на обчислення процесора.

Коротко:

  • IO-bound добре масштабується через asyncio/threads.
  • CPU-bound краще масштабується через multiprocessing/нативний код.
  • Спочатку визначте bottleneck через профілювання.
241. Для чого потрібен модуль `asyncio` у Python і як він дозволяє реалізувати асинхронне програмування?

Python

asyncio надає event loop, task scheduling і async primitives для кооперативної конкурентності в I/O-bound задачах.

Коротко:

  • Дозволяє ефективно обслуговувати багато I/O-операцій.
  • Базується на async/await.
  • Добре підходить для мережевих сервісів і клієнтів.
242. Чим відрізняється синхронне і асинхронне програмування у Python?

Python

Синхронне: виклик блокує поточний потік до завершення. Асинхронне: await віддає керування event loop, поки операція очікує I/O.

Коротко:

  • Async зменшує idle-time під час I/O.
  • Sync простіший для лінійної логіки.
  • Async додає складність управління задачами і cancellation.
243. Що таке `async`/`await`?

Python

async def визначає coroutine-функцію. await призупиняє coroutine до готовності awaitable і повертає контроль loop.

Коротко:

  • Це синтаксис асинхронної кооперативної моделі.
  • Використовується разом з asyncio та async-бібліотеками.
  • await можливий лише всередині async def.
244. Як працює `asyncio`?

Python

asyncio запускає event loop, який виконує tasks (coroutines), перемикаючись у точках await і плануючи готові операції I/O.

Коротко:

  • Один потік може обслуговувати багато I/O задач.
  • Планування кооперативне, не preemptive.
  • Блокувальні виклики потрібно виносити з loop.
245. Що таке event loop?

Python

Event loop це планувальник, який відстежує події/готовність I/O і запускає відповідні callbacks/coroutines.

Коротко:

  • Центральний компонент asyncio-моделі.
  • Керує життєвим циклом async задач.
  • Визначає, коли яка coroutine продовжує виконання.
246. Як `asyncio` дозволяє реалізувати асинхронне програмування і які основні компоненти задіяні в asyncio-коді?

Python

Ключові компоненти:

  • event loop;
  • coroutine (async def);
  • task (asyncio.create_task);
  • awaitables (futures, tasks, coroutines);
  • синхронізаційні примітиви (Lock, Queue, Semaphore).

Коротко:

  • asyncio поєднує планування і async-API в єдину модель.
  • Задачі кооперативно ділять один потік виконання.
  • Архітектура має враховувати таймаути, retry і cancellation.
247. Коли `asyncio` не дає переваг?

Python

Коли workload CPU-bound або основні бібліотеки блокувальні й не мають async API. Також не окупається для простих коротких скриптів.

Коротко:

  • Async не прискорює чисті обчислення.
  • Без неблокувальних I/O бібліотек вигода мінімальна.
  • Складність async має бути виправдана навантаженням.
248. Як працює asyncio cancellation?

Python

Скасування задачі (task.cancel()) піднімає CancelledError у coroutine. Код має коректно обробляти cleanup у try/finally.

Коротко:

  • Cancellation це звичайний control-flow у async.
  • Потрібно закладати обробку скасування в дизайн coroutine.
  • Ігнорування cancellation призводить до "завислих" задач.
249. Що таке `contextvars`?

Python

contextvars дає context-local змінні, безпечні для async/threads сценаріїв. Корисно для request-id, correlation-id, tenant-context.

Коротко:

  • Альтернатива глобальному state у конкурентному коді.
  • Значення ізольоване per-context.
  • Покращує трасування і observability.
250. Які best practices варто застосовувати при написанні Python-коду?

Python

  • дотримуватись PEP 8 та автоматизувати форматування;
  • писати явні type hints для публічного API;
  • використовувати with для ресурсів;
  • покривати бізнес-логіку тестами;
  • уникати передчасної оптимізації, профілювати;
  • тримати модулі невеликими з чіткою відповідальністю;
  • керувати залежностями через pyproject.toml + lock strategy.

Коротко:

  • Читабельність і передбачуваність важливіші за "трюки".
  • Якість тримається на автоматизації: lint, types, tests, CI.
  • Архітектурна простота зменшує вартість підтримки.

About

Найпопулярніші запитання та відповіді на співбесіді з Python

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

  •  

Packages

No packages published

Contributors 2

  •  
  •