python-interview-questions

Read in other languages: English 🇺🇸, Polska 🇵🇱, German 🇩🇪, French 🇫🇷, Spanish 🇪🇸, Українська 🇺🇦.

Python Python logo

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

1. Що таке Python і які його основні особливості? #### Python **Python** — це високорівнева мова загального призначення з акцентом на читабельність, швидку розробку та великий стандартний інструментарій. Основні особливості: - **Простий синтаксис**: код легко читати й підтримувати. - **Інтерпретована модель виконання**: швидкий цикл змін без етапу ручної компіляції. - **Кросплатформеність**: один код працює на Linux, macOS, Windows. - **Мультипарадигменність**: procedural, OOP, functional підходи в одному проєкті. - **Сильна екосистема**: `pip`, `venv`, `pyproject.toml`, тисячі готових бібліотек. - **Сучасні можливості Python 3.10+**: type hints, `dataclass`, `async/await`, `match/case`, генератори, context managers. Приклад production-style функції: ```python 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.10+ ключові практики: 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 Динамічна типізація означає, що тип прив'язаний до об'єкта, а не до імені змінної. Ім'я може посилатися на значення різних типів у різний час виконання. ```python value = 10 # int value = "10" # str ``` Типи перевіряються під час виконання, тому помилки типів виникають у runtime, якщо не використовувати статичний аналізатор типів. **Коротко:** - У Python тип має об'єкт, не змінна. - Тип може змінюватися при перевизначенні імені. - Type hints додають контроль до запуску коду.
5. Що означає строгість типів (strong typing) у Python? #### Python Strong typing у Python означає, що інтерпретатор не виконує небезпечні неявні перетворення між несумісними типами. ```python 1 + "2" # TypeError ``` Для операцій між різними типами потрібне явне приведення: ```python 1 + int("2") # 3 ``` **Коротко:** - Python динамічно типізований, але строгий щодо сумісності типів. - Небезпечні неявні конверсії блокуються помилкою. - Явне приведення робить поведінку передбачуваною.
6. Що таке REPL і коли його використовують? #### Python REPL (Read-Eval-Print Loop) це інтерактивний режим, де ви вводите вираз, отримуєте результат одразу і швидко перевіряєте гіпотези. Застосування: - перевірити API бібліотеки; - швидко протестувати вираз або алгоритм; - дослідити дані перед імплементацією в модулі. Інструменти: стандартний `python`, `ipython`, `ptpython`. **Коротко:** - REPL дає найшвидший фідбек-цикл. - Зручний для експериментів і дебагу. - Не замінює тести, але скорочує час перевірки ідей.
7. Що таке literals у Python, наведіть приклади різних типів literals? #### Python Literal це фіксоване значення, записане прямо в коді. ```python 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`. Переглянути список: ```python import keyword print(keyword.kwlist) ``` **Коротко:** - Keywords формують синтаксис Python. - Їх не можна використовувати як ідентифікатори. - Список доступний через модуль `keyword`.
9. Що таке змінна в Python? #### Python Змінна в Python це ім'я (reference), яке посилається на об'єкт у пам'яті. Присвоєння зв'язує ім'я з об'єктом, а не "кладе значення в коробку". ```python x = [1, 2] y = x y.append(3) # x і y посилаються на один список ``` **Коротко:** - Змінна це посилання на об'єкт. - Кілька імен можуть посилатися на один mutable-об'єкт. - Це важливо для розуміння копіювання і побічних ефектів.
10. Що таке `pass` і `...` (Ellipsis) у Python? #### Python `pass` це оператор-заглушка: нічого не робить, але дозволяє синтаксично коректний порожній блок. `...` (Ellipsis) це окремий об'єкт `Ellipsis`. Часто використовується як маркер "ще не реалізовано", у type hints та бібліотеках (наприклад NumPy). ```python 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. ```python 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`). Тобто відступ є частиною синтаксису, а не лише стилем. ```python 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. ```python 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 це швидкий спосіб перевірити значення змінних і порядок виконання без запуску складних інструментів. Корисно, коли треба швидко перевірити гіпотезу в локальному скрипті: ```python 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` дозволяє зупиняти виконання програми, крокувати по рядках, дивитися стан змінних і стек викликів. Базове використання: ```python 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` для цілих операндів. ```python 7 / 2 # 3.5 7 // 2 # 3 -7 // 2 # -4 ``` **Коротко:** - `/` -> дійсний результат. - `//` -> округлення вниз до цілого. - Для від'ємних чисел `//` не еквівалентне "обрізанню до нуля".
28. Поясніть використання оператора модуля (`%`) у Python. Як його можна використати щоб визначити, чи число парне або непарне? #### 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`. Це треба обробляти явно, якщо дільник надходить із зовнішнього вводу або розраховується динамічно. ```python 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`. ```python 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` — повернення каретки - `\"` — подвійна лапка в рядку з `"` - `\'` — одинарна лапка в рядку з `'` ```python text = "A\tB\n\"quoted\"\nI\'m here\rX" ``` **Коротко:** - Escape-символи керують форматуванням рядка. - Дозволяють вставляти лапки без ламання синтаксису. - Для шляхів/regex часто зручно використовувати raw-рядки `r"..."`.
34. Поясніть використання методів `strip`, `lstrip` та `rstrip` у Python. Чим вони відрізняються? #### Python Методи працюють з краями рядка: - `strip()` — прибирає символи з обох боків; - `lstrip()` — лише зліва; - `rstrip()` — лише справа. За замовчуванням видаляють whitespace, або конкретний набір символів: ```python " hello ".strip() # "hello" "--id--".strip("-") # "id" ``` **Коротко:** - `strip`-сімейство не змінює рядок in-place, а повертає новий. - Різниця лише у стороні очищення. - Часто використовується на вхідних даних.
35. Опишіть призначення методу `count` у рядках. Як він працює? #### Python `str.count(sub[, start[, end]])` рахує кількість неперекривних входжень підрядка `sub` у вибраному діапазоні. ```python "banana".count("an") # 2 "banana".count("a", 2) # 2 ``` Повертає `0`, якщо входжень немає. **Коротко:** - `count` швидко дає кількість входжень підрядка. - Підтримує обмеження пошуку через `start/end`. - Рахує неперекривні входження.
36. Як працює метод `join` у Python і для чого найчастіше його використовують? #### Python `sep.join(iterable)` з'єднує послідовність рядків в один рядок через розділювач `sep`. ```python names = ["Ada", "Linus", "Guido"] result = ", ".join(names) # "Ada, Linus, Guido" ``` Типове застосування: формування CSV-подібних рядків, логів, SQL-фрагментів, URL-шляхів, повідомлень. **Коротко:** - `join` це стандартний і швидкий спосіб склеювання рядків. - Викликається на розділювачі, не на списку. - Ефективніший за багаторазове `+=` у циклі.
37. Що таке slicing (нарізка) у Python і як з її допомогою отримати підрядки? Наведіть приклади з позитивними та негативними індексами. #### Python Slicing це вибір частини послідовності через `[start:stop:step]`. ```python 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`. За замовчуванням замінюються всі входження. ```python "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` обмежує кількість розділень. ```python "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()`. ```python 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`. ```python bool([]) # False bool("0") # True ``` **Коротко:** - Boolean-конверсія базується на truthy/falsy правилах. - Порожнє і нульове -> `False`. - Непорожній рядок `"0"` все одно `True`.
42. Поясніть, як перетворити рядок у ціле або дійсне число у Python. Що буде, якщо рядок не можна перетворити у число? #### Python Для перетворення використовуйте `int()` і `float()`: ```python count = int("42") ratio = float("3.14") ``` Якщо рядок невалідний, Python піднімає `ValueError`. ```python 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`. ```python 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` це різні типи з різною семантикою, тому пряме порівняння порядку між ними не підтримується. ```python [1, 2] == {1, 2} # False [1, 2] < {1, 2} # TypeError ``` Якщо треба порівняти склад елементів, нормалізуйте тип: ```python set([1, 2]) == {1, 2} # True ``` **Коротко:** - `list` і `set` порівнюються як різні структури даних. - `==` між ними зазвичай `False`. - Для змістовного порівняння спершу приводьте до одного типу.
45. Опишіть використання функції `bool()` у Python. #### Python `bool(x)` повертає булеве значення `x` за правилами truthiness. Типові сценарії: - явне перетворення значення до `True/False`; - читабельні умови; - побудова фільтрів. ```python bool("text") # True bool("") # False bool(0) # False ``` **Коротко:** - `bool()` уніфікує перевірку "порожнє/непорожнє". - Спирається на вбудовані правила truthy/falsy. - Корисний для валідації та умовної логіки.
46. У чому різниця між `list` і `tuple`? #### Python Головна різниця: `list` mutable, `tuple` immutable. Практичні наслідки: - `list` підходить для даних, що змінюються; - `tuple` підходить для фіксованих записів; - `tuple` можна використовувати як ключ словника (якщо елементи хешовані). ```python coords: tuple[float, float] = (50.45, 30.52) queue: list[str] = ["task-1", "task-2"] ``` **Коротко:** - `list` для змінних колекцій. - `tuple` для незмінних структур даних. - Вибір впливає на безпечність API і використання в `dict/set`.
47. Як можна створити `tuple`? #### Python Основні способи: ```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]` створює новий перевернутий список (копію). ```python 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` — спадний порядок. ```python nums = [5, 1, 7] nums.sort(reverse=True) # [7, 5, 1] ``` Якщо потрібна нова відсортована копія, використовуйте `sorted(iterable)`. **Коротко:** - `sort()` змінює список на місці. - Для спадання використовуйте `reverse=True`. - `sorted()` зручний, коли оригінал треба зберегти.
50. У чому різниця між методом `copy` і прямим присвоєнням одного списку іншому? Чому іноді важливо використовувати `copy`? #### Python Присвоєння копіює лише посилання: ```python a = [1, 2] b = a b.append(3) # a теж зміниться ``` `a.copy()` створює новий (поверхневий) список: ```python a = [1, 2] b = a.copy() b.append(3) # a не зміниться ``` Для вкладених структур може знадобитися `copy.deepcopy`. **Коротко:** - `=` не копіює дані, а ділить один об'єкт між змінними. - `copy()` ізолює зміни на рівні верхнього списку. - Для вкладених об'єктів використовуйте `deepcopy`.
51. Що робить метод `pop` у списку? Чим його поведінка відрізняється якщо вказати індекс і якщо ні? #### Python `pop()` видаляє і повертає елемент зі списку. - `pop()` без аргументів видаляє останній елемент; - `pop(i)` видаляє елемент за індексом `i`. ```python 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**. ```python 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. ```python 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`), без винятку. ```python 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)`. ```python 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 Створення: ```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()` об'єднує всі унікальні елементи з обох множин. ```python 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`: ```python if value is None: ... ``` **Коротко:** - `None` означає відсутність значення. - Це singleton, тому порівнюють через `is`. - Часто використовується в API як опціональний стан.
67. Що таке `id` у Python, як його використовувати і чому це важливо? #### Python `id(obj)` повертає ідентифікатор об'єкта (унікальний в межах життєвого циклу об'єкта в поточному процесі). Корисно для діагностики: - чи це той самий об'єкт; - чи створено копію; - чи сталася мутація спільного reference. **Коротко:** - `id` допомагає аналізувати identity об'єктів. - Корисний у дебазі копіювання/мутацій. - Не використовується як бізнес-ідентифікатор.
68. Яка різниця між операторами `is` та `==`? #### Python `==` порівнює **значення** (еквівалентність), а `is` порівнює **ідентичність** (чи це один і той самий об'єкт у пам'яті). ```python a = [1, 2] b = [1, 2] a == b # True a is b # False ``` **Важливо про оптимізації (Interning):** CPython кешує ("інтернує") малі цілі числа (від -5 до 256) та короткі рядки на етапі компіляції/завантаження. Тому для них `is` може повертати `True`, навіть якщо вони створені окремо. Але це деталі реалізації, на які не варто покладатися в бізнес-логіці. Для `None` завжди використовуйте `is`. **Коротко:** - `==` про рівність значень. - `is` про той самий об'єкт у пам'яті. - Interning може давати неочевидний `is True` для малих `int` та `str`. - `value is None` — це єдиний правильний стиль для перевірки на `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` пропускає поточну ітерацію і переходить до наступної. ```python 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. ```python 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+) це механізм розбору структури даних за шаблонами. Працює з літералами, типами, послідовностями, словниками та класами. ```python 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-блок коду, який приймає аргументи, повертає результат і дозволяє інкапсулювати логіку. ```python 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`). ```python def f(a, /, b, *, c, **kwargs): ... ``` **Коротко:** - Python дає гнучкий контроль способу виклику функції. - `/` і `*` формалізують API-контракт. - `*args/**kwargs` корисні для розширюваних інтерфейсів.
77. Що таке позиційні та іменовані аргументи? #### Python Позиційні аргументи передаються за порядком, іменовані (`keyword`) за назвою параметра. ```python def connect(host: str, port: int) -> str: return f"{host}:{port}" connect("localhost", 5432) # позиційно connect(host="localhost", port=5432) # іменовано ``` **Коротко:** - Позиційні залежать від порядку параметрів. - Іменовані підвищують читабельність виклику. - Їх можна комбінувати з дотриманням правил сигнатури.
78. Що таке аргументи за замовчуванням і які з ними можуть бути проблеми? #### Python Default-аргументи підставляються, якщо значення не передано під час виклику. Вони обчислюються **один раз** при визначенні функції. Проблема: mutable default. ```python 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`. ```python def log_event(event: str, *args: object, **kwargs: object) -> None: ... ``` Використання: обгортки, адаптери API, декоратори, проксування параметрів. **Коротко:** - `*args` = додаткові позиційні. - `**kwargs` = додаткові іменовані. - Вони роблять функції гнучкими, але потребують чіткої валідації.
80. Як визначити функцію з type annotations у Python? Наведіть приклад і поясніть переваги цього підходу. #### Python Типи задаються в сигнатурі параметрів і return-значення. ```python 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`. ```python users = [{"name": "Ada"}, {"name": "Bob"}] users_sorted = sorted(users, key=lambda u: u["name"]) ``` Для складної логіки краще звичайна `def`-функція. **Коротко:** - `lambda` зручна для коротких локальних виразів. - Обмежена одним виразом. - Для читабельності складний код краще виносити в `def`.
82. Яка область видимості змінних у функції? #### Python Python використовує правило LEGB для пошуку імен: - `L`ocal; - `E`nclosing (зовнішні функції); - `G`lobal (модуль); - `B`uiltins. Усередині функції присвоєння створює локальну змінну, якщо не оголошено `global` або `nonlocal`. **Коротко:** - Scope визначає, де ім'я доступне і змінюване. - LEGB пояснює порядок пошуку змінних. - Неправильне розуміння scope часто дає `UnboundLocalError`.
83. Що таке локальні та глобальні змінні у Python? #### Python Локальні змінні живуть у тілі функції. Глобальні змінні визначені на рівні модуля. Для зміни глобальної з функції потрібен `global`, але це зазвичай варто уникати через неявні залежності. **Коротко:** - Локальні безпечніші для підтримки й тестування. - Глобальні спрощують доступ, але ускладнюють контроль стану. - Краще передавати залежності параметрами.
84. Яка різниця між локальними і nonlocal-змінними у Python? #### Python `nonlocal` використовується у вкладеній функції для зміни змінної із найближчого enclosing-scope (не глобального). ```python from collections.abc import Callable def counter() -> Callable: value = 0 def inc() -> int: nonlocal value value += 1 return value return inc ``` **Коротко:** - Локальна змінна належить поточній функції. - `nonlocal` змінює стан зовнішньої функції. - Це ключовий механізм для closure зі станом.
85. Що таке unpacking у Python і як його застосовують при присвоєнні? #### Python Unpacking це розкладання елементів послідовності/структури в окремі змінні. ```python x, y = (10, 20) first, *middle, last = [1, 2, 3, 4] ``` Працює також зі словниками в `match/case`, викликах функцій і циклах. **Коротко:** - Unpacking робить код компактнішим і читабельнішим. - Підтримує "зірочковий" збір решти значень. - Часто використовується в парсингу структур даних.
86. Поясніть поняття packing значень у Python і наведіть приклади. #### Python Packing це збирання кількох значень в одну структуру (`tuple`, `list`, `dict`). ```python point = 10, 20 # tuple packing def collect(*args: int) -> tuple[int, ...]: return args ``` `*args` і `**kwargs` це типовий приклад packing аргументів. **Коротко:** - Packing збирає багато значень в один контейнер. - Найчастіше використовується в сигнатурах функцій. - Добре поєднується з unpacking на стороні виклику.
87. Для чого використовують оператор `*` при packing та unpacking? #### Python `*` у параметрах функції пакує позиційні аргументи (`*args`), а у виклику розпаковує iterable в позиційні аргументи. ```python 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, який приймає функцію/клас і повертає модифіковану версію (обгортку). ```python 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` виконується першим обгортанням). ```python @decorator_a @decorator_b def handler() -> None: ... ``` Еквівалент: `handler = decorator_a(decorator_b(handler))`. **Коротко:** - Кілька декораторів допустимі і поширені. - Порядок застосування має значення. - Стек декораторів треба документувати для прозорості.
91. Опишіть можливу проблему з черговістю decorator-ів при їх застосуванні до функції. #### Python Неправильний порядок може змінити семантику: наприклад, кешування до перевірки доступу може закешувати небажаний результат або обійти очікувану логіку. Типові ризики: - логування бачить вже змінені аргументи; - ретраї обгортають не той виняток; - кеш ставиться до/після валідації в неправильній точці. **Коротко:** - Порядок декораторів впливає на поведінку функції. - Це часта причина прихованих багів. - Для критичних ланцюжків потрібні тести на порядок виконання.
92. Чи можна створити decorator за допомогою класу? #### Python Так. Клас-декоратор реалізує `__call__`, щоб екземпляр поводився як функція. ```python 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. ```python 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 створює колекцію з виразу і циклу(ів), опційно з фільтром. ```python 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 матриці: ```python matrix = [[1, 2], [3, 4], [5, 6]] flat = [item for row in matrix for item in row] # [1, 2, 3, 4, 5, 6] ``` Приклад з умовою: ```python 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. ```python items = [10, 20, 30] it = iter(items) ``` Після цього значення читаються через `next(it)`. **Коротко:** - `iter()` перетворює iterable на iterator. - Це явний спосіб керувати ітерацією вручну. - Використовується у низькорівневій обробці потоку даних.
101. Для чого призначена функція `next()` при роботі з iterator-ами? #### Python `next(iterator[, default])` повертає наступний елемент iterator. Коли елементи закінчились, кидає `StopIteration` або повертає `default`, якщо його передано. ```python 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`. ```python 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) для створення об'єктів з атрибутами та методами. ```python class User: def __init__(self, name: str) -> None: self.name = name ``` Клас задає структуру і поведінку майбутніх екземплярів. **Коротко:** - Клас описує дані й операції над ними. - На основі класу створюються об'єкти (instances). - Це базова одиниця моделювання в OOP.
110. Як створити object у Python? #### Python Об'єкт створюється викликом класу: ```python class User: def __init__(self, name: str) -> None: self.name = name user = User("Ada") ``` Під час створення виконується `__init__` для ініціалізації стану. **Коротко:** - Object = екземпляр класу. - Створення: `instance = ClassName(...)`. - `__init__` налаштовує початкові атрибути.
111. Що таке об'єкти та атрибути? #### Python Об'єкт це конкретний екземпляр типу/класу. Атрибут це пов'язане з об'єктом ім'я-значення (дані або метод). ```python user.name # атрибут-дані user.save() # атрибут-метод ``` **Коротко:** - Об'єкт зберігає стан і поведінку. - Атрибути описують цей стан/поведінку. - Доступ до атрибутів: крапкова нотація.
112. Яку роль виконує метод `__init__()` у класі? #### Python `__init__` це ініціалізатор екземпляра: викликається після створення об'єкта і заповнює початковий стан. ```python 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` це посилання на поточний екземпляр, через нього метод читає/змінює інстанс-атрибути. ```python def deposit(self, amount: float) -> None: self.balance += amount ``` Ім'я `self` не зарезервоване синтаксично, але є загальноприйнятим стандартом. **Коротко:** - `self` зв'язує метод з конкретним об'єктом. - Через `self` доступні інстанс-дані. - Це обов'язковий перший параметр instance method.
114. Як визначати методи у класі? #### Python Методи визначаються як функції всередині `class`. ```python 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 майже все є об'єктом: числа, рядки, функції, класи, модулі. Тому у всього є тип, атрибути й поведінка. ```python type(10) # <class 'int'> type(len) # <class 'builtin_function_or_method'> type(str) # <class 'type'> ``` **Коротко:** - Єдина об'єктна модель спрощує мову. - Функції й класи теж об'єкти першого класу. - Це робить Python гнучким для метапрограмування.
116. Наведіть приклад класу з методами, які виконують обчислення на основі атрибутів. #### Python ```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-методів. ```python 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`. Він логічно належить класу, але не залежить від стану. ```python 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`). Класові атрибути належать класу і спільні для всіх екземплярів. ```python 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__` для екземплярів. ```python class Point: __slots__ = ("x", "y") def __init__(self, x: int, y: int) -> None: self.x = x self.y = y ``` **Нюанси використання:** - **Економія пам'яті:** об'єкти займають значно менше місця, бо атрибути зберігаються у фіксованому масиві, а не в хеш-таблиці `__dict__`. - **Швидкість:** доступ до атрибутів у `__slots__` зазвичай трохи швидший. - **Відсутність `__dict__`:** ви не зможете динамічно додавати нові атрибути, яких немає у списку `__slots__` (якщо тільки не додати `"__dict__"` у сам список). - **Слабкі посилання:** якщо ви хочете використовувати `weakref`, потрібно явно додати `"__weakref__"` у `__slots__`. **Коротко:** - `__slots__` корисний для мільйонів легковагових об'єктів (напр. вузли графа). - Прибирає `__dict__` і `__weakref__` за замовчуванням. - Зменшує гнучкість на користь продуктивності та контролю.
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 ```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__`, ...), щоб об'єкти власного класу підтримували оператори. ```python 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 означає, що клас має лише одного прямого батька. ```python class Animal: ... class Dog(Animal): ... ``` Це найпростіша і зазвичай найчитабельніша форма наслідування. **Коротко:** - Один дочірній клас -> один базовий клас. - Проста модель резолюції методів. - Часто достатня для більшості бізнес-моделей.
131. Як реалізувати наслідування у класах Python і який для цього використовується синтаксис? #### Python Синтаксис: `class Child(Base):`. ```python class Base: def greet(self) -> str: return "hello" class Child(Base): def greet(self) -> str: return "hi" ``` За потреби виклику базової логіки використовуйте `super()`. **Коротко:** - Наслідування задається у дужках після імені класу. - Дочірній клас отримує API базового. - Перевизначення дозволяє адаптувати поведінку.
132. Як отримати доступ до членів базового класу у дочірньому класі? #### Python Доступ можливий напряму через успадковані атрибути/методи або через `super()`. ```python 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. ```python 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)` перевіряє, чи об'єкт належить типу або його підкласу. ```python 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`. ```python class Animal: ... class Dog(Animal): ... issubclass(Dog, Animal) # True ``` **Коротко:** - Працює з класами, не з екземплярами. - Зручно для валідації API на рівні типів. - Враховує транзитивне наслідування.
136. Що таке multiple inheritance (множинне наслідування)? #### Python Multiple inheritance це спадкування одного класу від кількох базових класів. ```python class A: ... class B: ... class C(A, B): ... ``` Дає гнучкість, але вимагає дисципліни у дизайні методів і `super()`. **Коротко:** - Один клас може успадковувати поведінку з кількох джерел. - Корисно для mixin-підходу. - Може ускладнювати розуміння MRO.
137. Як працює MRO (Method Resolution Order) у multiple inheritance? #### Python MRO визначає порядок пошуку методів у ієрархії класів. У Python використовується алгоритм C3 linearization. **Diamond Problem (Проблема алмазу):** Це класичний випадок, коли клас `D` успадковується від `B` та `C`, які обидва успадковуються від `A`. MRO гарантує, що `A` буде переглянутий лише після того, як будуть переглянуті всі його нащадки (`B` та `C`). ```python class A: pass class B(A): pass class C(A): pass class D(B, C): pass print(D.mro()) # [D, B, C, A, object] ``` Переглянути порядок можна через `ClassName.__mro__` або `ClassName.mro()`. **Коротко:** - MRO вирішує, з якого базового класу брати метод. - Порядок передбачуваний і формально визначений (C3). - Завдяки MRO Python коректно обробляє Diamond Problem. - Для 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 і протоколами. ```python 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-логіці. ```python 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` позначає метод, який обов'язково має реалізувати підклас. ```python from abc import ABC, abstractmethod class Storage(ABC): @abstractmethod def save(self, data: bytes) -> None: ... ``` **Коротко:** - ABC формалізує контракт ієрархії. - `@abstractmethod` забороняє інстанціювати неповну реалізацію. - Корисно для плагінних/розширюваних архітектур.
146. Що таке property у Python і як їх застосовують? #### Python `property` перетворює методи доступу в атрибутоподібний API з можливістю валідації, обчислень або lazy-логіки. ```python 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)` — перевірити наявність. ```python 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-у знати ім'я атрибута, до якого його прив'язано. ```python class Field: def __set_name__(self, owner, name): self.name = name ``` **Коротко:** - `__set_name__` ініціалізує descriptor контекстом класу. - Дозволяє робити перевикористовувані валідатори полів. - Спрацьовує один раз на етапі class creation.
153. Що таке `dataclass` і коли його варто використовувати? #### Python `@dataclass` автоматично генерує boilerplate (`__init__`, `__repr__`, `__eq__`). Підходить для моделей даних без складної поведінки. ```python 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` описує типізовану форму словника: які ключі очікуються і яких типів їх значення. ```python 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). ```python 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` (або конкретніший базовий виняток). ```python 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`). ```python 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 Запис: ```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__` при виході, навіть коли сталася помилка. ```python with open("data.txt", "r", encoding="utf-8") as f: data = f.read() ``` **Коротко:** - `with` гарантує cleanup. - Робить роботу з ресурсами декларативною. - Рекомендований для файлів, локів, транзакцій, сесій.
182. Як створити власний контекстний менеджер? #### Python Два підходи: - клас з `__enter__`/`__exit__`; - функція з `contextlib.contextmanager`. ```python 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, і всі посилання бачать зміну. ```python 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 рекурсивно копіює всю структуру. ```python 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. ```python 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 перемішування. ```python import random items = [1, 2, 3, 4] random.shuffle(items) ``` **Коротко:** - `shuffle` змінює оригінальний список. - Працює лише з мутабельними послідовностями (наприклад, списками). - Для нової копії: `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. - Часто генерується інструментами (наприклад, `pip-tools` або `poetry export`) з декларативного переліку/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)` перевіряє, що блок коду піднімає саме очікуваний виняток. ```python import pytest with pytest.raises(ValueError): int("abc") ``` **Коротко:** - Тестує негативні сценарії явно. - Захищає від "мовчазного" проходження помилок. - Може перевіряти і повідомлення/атрибути винятку.
216. Що таке параметризація у тестах, як pytest підтримує її через `@pytest.mark.parametrize`? #### Python Параметризація запускає один тест на кількох наборах вхідних даних. У pytest це робиться декоратором `@pytest.mark.parametrize`. **Коротко:** - Менше дублювання тест-коду. - Легко масштабувати кейси. - Краща прозорість покриття вхідних сценаріїв.
217. Як можна у pytest задати власні назви тестів при параметризованому тестуванні для кращої читабельності? #### Python Використовуйте параметр `ids` у `parametrize`. ```python @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. **Критичне правило:** Event loop працює в одному потоці. Будь-яка блокувальна операція (`time.sleep()`, синхронні запити `requests`, важкі обчислення) зупиняє **весь** цикл і всі інші задачі. **Коротко:** - Один потік може обслуговувати багато I/O задач через кооперативність. - Планування не витісняє задачі (non-preemptive). - Блокувальні виклики вбивають продуктивність asyncio.
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-середовищі, використовуйте `loop.run_in_executor(None, sync_func)`, що запустить її в окремому потоці, не блокуючи event loop. **Коротко:** - Async не прискорює чисті обчислення. - Без неблокувальних I/O бібліотек вигода мінімальна. - `run_in_executor` допомагає інтегрувати legacy/sync код. - Складність 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. - Архітектурна простота зменшує вартість підтримки.