1. Що таке Python і які його основні особливості?
Python — це високорівнева мова загального призначення з акцентом на читабельність, швидку розробку та великий стандартний інструментарій.
Основні особливості:
- Простий синтаксис: код легко читати й підтримувати.
- Інтерпретована модель виконання: швидкий цикл змін без етапу ручної компіляції.
- Кросплатформеність: один код працює на Linux, macOS, Windows.
- Мультипарадигменність: procedural, OOP, functional підходи в одному проєкті.
- Сильна екосистема:
pip,venv,pyproject.toml, тисячі готових бібліотек. - Сучасні можливості Python 3.14+: type hints,
dataclass,async/await,match/case, генератори, context managers.
Приклад production-style функції:
from dataclasses import dataclass
@dataclass(slots=True)
class User:
name: str
active: bool
def process_users(users: list[User]) -> list[str]:
return [user.name for user in users if user.active]Коротко:
- Python прискорює розробку завдяки простому синтаксису та великій стандартній бібліотеці.
- Мова підходить для backend, automation, data/ML, тестування та DevOps.
- У Python 3.14+ ключові практики: type hints, async IO, сучасний standard library.
2. Які ключові переваги Python?
Ключові переваги Python в продакшені:
- висока швидкість розробки через лаконічний синтаксис;
- велика стандартна бібліотека (
pathlib,itertools,collections,asyncio); - зріла екосистема пакетів для backend, data, automation, тестування;
- переносимість коду між ОС;
- хороша підтримка типізації (
typing,mypy,pyright) і сучасних практик.
Коротко:
- Python зменшує time-to-market.
- Дає багато готових інструментів без додаткових залежностей.
- Підходить і для MVP, і для масштабованих сервісів.
3. Чим Python відрізняється від компільованих мов програмування?
Python зазвичай виконується інтерпретатором: код компілюється у байткод і виконується в рантаймі (CPython VM). У компільованих мовах (C/C++, Rust) переважно є попередня компіляція у машинний код.
Практичні наслідки:
- Python швидше в розробці та прототипуванні.
- Нативні компільовані мови зазвичай швидші у CPU-bound задачах.
- У Python продуктивність часто покращують алгоритмами, профілюванням і C-розширеннями.
Коротко:
- Python оптимізує швидкість розробки.
- Компіляція в машинний код зазвичай дає кращу raw-продуктивність.
- Вибір залежить від домену та вимог до latency/throughput.
4. Що означає динамічна типізація в Python?
Динамічна типізація означає, що тип прив'язаний до об'єкта, а не до імені змінної. Ім'я може посилатися на значення різних типів у різний час виконання.
value = 10 # int
value = "10" # strТипи перевіряються під час виконання, тому помилки типів виникають у runtime, якщо не використовувати статичний аналізатор типів.
Коротко:
- У Python тип має об'єкт, не змінна.
- Тип може змінюватися при перевизначенні імені.
- Type hints додають контроль до запуску коду.
5. Що означає строгість типів (strong typing) у Python?
Strong typing у Python означає, що інтерпретатор не виконує небезпечні неявні перетворення між несумісними типами.
1 + "2" # TypeErrorДля операцій між різними типами потрібне явне приведення:
1 + int("2") # 3Коротко:
- Python динамічно типізований, але строгий щодо сумісності типів.
- Небезпечні неявні конверсії блокуються помилкою.
- Явне приведення робить поведінку передбачуваною.
6. Що таке REPL і коли його використовують?
REPL (Read-Eval-Print Loop) це інтерактивний режим, де ви вводите вираз, отримуєте результат одразу і швидко перевіряєте гіпотези.
Застосування:
- перевірити API бібліотеки;
- швидко протестувати вираз або алгоритм;
- дослідити дані перед імплементацією в модулі.
Інструменти: стандартний python, ipython, ptpython.
Коротко:
- REPL дає найшвидший фідбек-цикл.
- Зручний для експериментів і дебагу.
- Не замінює тести, але скорочує час перевірки ідей.
7. Що таке literals у Python, наведіть приклади різних типів literals?
Literal це фіксоване значення, записане прямо в коді.
name = "Ada" # str literal
count = 42 # int literal
ratio = 3.14 # float literal
enabled = True # bool literal
items = [1, 2, 3] # list literal
config = {"retries": 3} # dict literal
flags = {"a", "b"} # set literal
point = (10, 20) # tuple literal
raw = b"abc" # bytes literalКоротко:
- Literals це вбудовані константні значення в коді.
- Кожен базовий тип має свій синтаксис літерала.
- Вони часто використовуються для ініціалізації даних.
8. Що таке keywords у Python?
Keywords це зарезервовані слова мови, які мають спеціальне синтаксичне призначення і не можуть бути іменами змінних.
Приклади: if, for, class, def, match, case, try, except,
async, await.
Переглянути список:
import keyword
print(keyword.kwlist)Коротко:
- Keywords формують синтаксис Python.
- Їх не можна використовувати як ідентифікатори.
- Список доступний через модуль
keyword.
9. Що таке змінна в Python?
Змінна в Python це ім'я (reference), яке посилається на об'єкт у пам'яті. Присвоєння зв'язує ім'я з об'єктом, а не "кладе значення в коробку".
x = [1, 2]
y = x
y.append(3)
# x і y посилаються на один списокКоротко:
- Змінна це посилання на об'єкт.
- Кілька імен можуть посилатися на один mutable-об'єкт.
- Це важливо для розуміння копіювання і побічних ефектів.
10. Що таке `pass` і `...` (Ellipsis) у Python?
pass це оператор-заглушка: нічого не робить, але дозволяє синтаксично
коректний порожній блок.
... (Ellipsis) це окремий об'єкт Ellipsis. Часто використовується як
маркер "ще не реалізовано", у type hints та бібліотеках (наприклад NumPy).
def feature() -> None:
pass
PLACEHOLDER = ...Коротко:
passпотрібен для порожніх блоків коду....це значення-об'єкт, а не ключове слово.- Обидва використовують як тимчасові заглушки з різною семантикою.
11. Що таке PEP і як він впливає на розвиток Python?
PEP (Python Enhancement Proposal) це офіційний документ, який описує нову фічу, стандарт або процес у екосистемі Python.
Через PEP проходять:
- обговорення дизайну;
- технічна аргументація;
- рішення про прийняття/відхилення;
- стандартизація практик.
Приклади: PEP 8 (style), PEP 484 (type hints), PEP 634 (pattern matching).
Коротко:
- PEP це механізм еволюції мови та інструментів.
- Він робить зміни прозорими й формалізованими.
- Багато сучасних практик Python закріплені саме через PEP.
12. Що таке PEP 8 і навіщо він потрібен?
PEP 8 це офіційний стиль-гайд для Python-коду: іменування, форматування, відступи, імпорти, довжина рядків, читабельність конструкцій.
Навіщо потрібен:
- код має однаковий стиль у команді;
- швидше code review;
- менше шуму в diff;
- вища підтримуваність.
На практиці стиль автоматизують через ruff format або black + ruff.
Коротко:
- PEP 8 стандартизує стиль Python-коду.
- Це про читабельність і підтримку, не про продуктивність.
- Дотримання краще автоматизувати інструментами.
13. Які нові можливості з'явилися у сучасних версіях Python (3.10+)?
Ключові можливості сучасного 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?
Docstring це рядок документації, перший вираз у модулі, класі або функції.
Він доступний через __doc__ і використовується IDE, help(), генераторами docs.
def normalize_email(email: str) -> str:
"""Return lowercase email without surrounding spaces."""
return email.strip().lower()Рекомендовано описувати: що робить функція, аргументи, повернення, винятки.
Коротко:
- Docstring це вбудована документація в коді.
- Працює як джерело для
help()та автогенерації docs. - Якісний docstring зменшує потребу читати реалізацію.
15. Як працюють відступи (indentation) у Python?
У Python відступи визначають блоки коду (тіло if, for, def, class).
Тобто відступ є частиною синтаксису, а не лише стилем.
if is_ready:
run_task()
else:
stop_task()Змішування tab і spaces може призвести до IndentationError або некоректної
структури блоку.
Коротко:
- Відступ у Python задає структуру програми.
- Неправильний відступ ламає виконання.
- Використовуйте стабільний стиль (4 пробіли).
16. Скільки пробілів потрібно для відступу відповідно до PEP8 та чому важливо дотримуватись однакового відступу?
PEP 8 рекомендує 4 пробіли на один рівень відступу.
Чому це критично:
- однакова структура блоку в усьому проєкті;
- передбачуваний вигляд у різних редакторах;
- менше помилок через tab/space конфлікти;
- чистіший diff у Git.
Коротко:
- Стандартний відступ: 4 пробіли.
- Єдиний стиль прибирає клас помилок форматування.
- Це напряму покращує читабельність і підтримку коду.
17. У чому різниця між `ruff` і `black` щодо функціоналу та використання?
black це форматер коду з фіксованими правилами.
ruff це швидкий linter, а також форматер і автофіксер багатьох правил
(PEP8, імпорти, потенційні баги, спрощення синтаксису).
Практичні підходи:
- або
ruff check --fix+ruff format; - або
ruffдля lint +blackдля форматування.
Коротко:
blackфокусується на форматуванні.ruffзакриває linting і частину автоправок.- У сучасних проєктах часто достатньо одного
ruff.
18. Поясніть призначення type annotations у Python і наведіть приклад функції з type annotations.
Type annotations описують очікувані типи аргументів і повернення. Вони покращують автодоповнення, статичний аналіз і читабельність API.
def process_users(users: list[dict[str, object]]) -> list[str]:
return [str(user["name"]) for user in users if bool(user.get("active"))]Анотації не виконують runtime-валідацію самі по собі, але допомагають знайти
помилки на етапі CI через mypy/pyright.
Коротко:
- Type hints документують контракт функції.
- Дають раннє виявлення помилок через static checking.
- Підвищують якість API у великих кодових базах.
19. Що таке debugging і чому це важливий навик для програмістів?
Debugging це системний пошук причини некоректної поведінки коду та її усунення. Це не лише "знайти баг", а відтворити, локалізувати і перевірити fix.
Базовий цикл:
- відтворити проблему;
- звузити ділянку коду;
- перевірити гіпотезу (логами/дебагером);
- додати тест, що фіксує сценарій.
Коротко:
- Debugging зменшує MTTR і кількість регресій.
- Працює найкраще як процес, а не хаотичний пошук.
- Завершення дебагу: fix + тест + постперевірка.
20. Поясніть призначення використання функції `print` для налагодження. Наведіть приклад ситуації, коли цей підхід корисний.
print-debugging це швидкий спосіб перевірити значення змінних і порядок
виконання без запуску складних інструментів.
Корисно, коли треба швидко перевірити гіпотезу в локальному скрипті:
def parse_port(raw: str) -> int:
value = raw.strip()
print(f"raw={raw!r} value={value!r}")
return int(value)Для продакшен-коду краще переходити на logging, щоб мати рівні логів і
кероване виведення.
Коротко:
printпідходить для короткої локальної діагностики.- Швидко показує стан змінних у проблемній точці.
- Для стабільної діагностики в сервісі використовуйте
logging.
21. Опишіть, як можна використовувати Python Debugger (PDB) для налагодження. Які переваги PDB у порівнянні з `print`?
pdb дозволяє зупиняти виконання програми, крокувати по рядках, дивитися стан
змінних і стек викликів.
Базове використання:
import pdb
def compute(x: int) -> int:
pdb.set_trace()
return x * 2Ключові команди: n (next), s (step), c (continue), p expr (print expr),
l (list), bt (backtrace).
Коротко:
pdbдає інтерактивний контроль виконання.- Ефективніший за
printдля складних сценаріїв. - Дозволяє діагностувати стан без засмічення коду логами.
22. Які стратегії можна застосовувати для налагодження циклів та функцій у Python?
Практичні стратегії:
- локалізувати баг через мінімальний відтворюваний приклад;
- логувати інваріанти на ітераціях циклу;
- ставити breakpoint на вхід/вихід функції;
- перевіряти крайні випадки (порожні дані,
None, великі обсяги); - покривати проблемний шлях тестом.
Для циклів корисно логувати індекс і ключові проміжні стани.
Коротко:
- Починайте з відтворення і звуження проблеми.
- Перевіряйте інваріанти і крайні умови.
- Фіналізуйте фікс тестом, що ловить регресію.
23. Який вплив мають довготривалі функції на продуктивність програми?
Довготривалі функції збільшують latency, блокують воркери/потоки і погіршують пропускну здатність сервісу.
Ризики:
- повільні відповіді API;
- таймаути;
- зростання черг задач;
- гірше використання CPU/IO.
Підхід: профілювати (cProfile), розбивати на менші кроки, використовувати
генератори/стрімінг, виносити важкі обчислення в multiprocessing або фон.
Коротко:
- Довгі функції напряму б'ють по latency.
- Оптимізацію роблять на основі профілювання, не інтуїції.
- Архітектурно краще розділяти CPU-bound і IO-bound роботу.
24. Що таке logging і чому його краще використовувати замість `print`?
logging це стандартний механізм журналювання подій із рівнями (DEBUG,
INFO, WARNING, ERROR, CRITICAL), форматами й обробниками (console, file).
Чому краще за print:
- керовані рівні деталізації;
- структурований формат;
- централізована конфігурація;
- можливість інтеграції з observability-стеком.
Коротко:
loggingпридатний для продакшену,printні.- Рівні логів дають контроль шуму.
- Логи мають бути структурованими й консистентними.
25. Що таке traceback?
Traceback це стек викликів, який Python показує при винятку: де почалось виконання, через які функції пройшов код і де саме сталася помилка.
Використання:
- швидко знайти файл/рядок джерела помилки;
- зрозуміти шлях виконання до збою;
- побудувати точний тест на відтворення.
Коротко:
- Traceback це "маршрут" до помилки.
- Найцінніша частина: останні кадри стека і тип винятку.
- Аналіз traceback прискорює root-cause.
26. Які основні типи даних існують у 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?
/ виконує звичайне ділення і завжди повертає float.
// виконує floor-ділення: повертає цілу частину вниз (до -∞), тип зазвичай
int для цілих операндів.
7 / 2 # 3.5
7 // 2 # 3
-7 // 2 # -4Коротко:
/-> дійсний результат.//-> округлення вниз до цілого.- Для від'ємних чисел
//не еквівалентне "обрізанню до нуля".
28. Поясніть використання оператора модуля (`%`) у Python. Як його можна використати щоб визначити, чи число парне або непарне?
Оператор % повертає остачу від ділення.
Перевірка парності:
def is_even(n: int) -> bool:
return n % 2 == 0Якщо n % 2 == 0, число парне; інакше непарне.
Типові задачі: циклічні індекси, розбиття на групи, календарні обчислення.
Коротко:
%повертає remainder.n % 2це стандартна перевірка парності.- Часто використовується для циклічної логіки.
29. Як Python працює з великими цілими числами і які є обмеження при роботі з дуже великими числами?
int у Python має довільну точність (arbitrary precision), тобто не обмежений
32/64-бітами як у багатьох мовах.
Обмеження практичні:
- пам'ять процесу;
- час обчислень на дуже великих значеннях;
- вартість операцій зростає зі збільшенням кількості цифр.
Коротко:
- Переповнення
intу звичному сенсі немає. - Обмеження: ресурси машини, а не фіксований бітовий розмір.
- Для великих обчислень важливі алгоритми і профілювання.
30. Яке значення має обробка ділення на нуль у Python, як запобігти `ZeroDivisionError` у коді?
Ділення на нуль викликає ZeroDivisionError. Це треба обробляти явно, якщо
дільник надходить із зовнішнього вводу або розраховується динамічно.
def safe_div(a: float, b: float) -> float | None:
if b == 0:
return None
return a / bУ критичних сценаріях використовуйте try/except і логування контексту.
Коротко:
- Ділення на нуль це контрольована runtime-помилка.
- Найпростіше запобігання: перевірка дільника до операції.
- Для зовнішніх даних додавайте захист і діагностику.
31. Опишіть використання модуля `random` у 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 працювати з числами з кращою точністю?
Для фінансових і точних десяткових обчислень використовуйте decimal.Decimal,
а не float.
from decimal import Decimal
total = Decimal("0.1") + Decimal("0.2") # Decimal('0.3')Для раціональних чисел корисний fractions.Fraction.
Коротко:
floatзручний, але має похибки двійкового представлення.- Для точної десяткової арифметики обирайте
Decimal. - Тип числа має відповідати домену задачі.
33. Що таке escape-символи у рядках Python і як їх використовують? Наведіть приклади для `\n`, `\t`, `\r`, `\"`, і `\'`.
Escape-послідовності це спеціальні комбінації з \, які кодують службові
символи всередині рядка.
\n— новий рядок\t— табуляція\r— повернення каретки\"— подвійна лапка в рядку з"\'— одинарна лапка в рядку з'
text = "A\tB\n\"quoted\"\nI\'m here\rX"Коротко:
- Escape-символи керують форматуванням рядка.
- Дозволяють вставляти лапки без ламання синтаксису.
- Для шляхів/regex часто зручно використовувати raw-рядки
r"...".
34. Поясніть використання методів `strip`, `lstrip` та `rstrip` у Python. Чим вони відрізняються?
Методи працюють з краями рядка:
strip()— прибирає символи з обох боків;lstrip()— лише зліва;rstrip()— лише справа.
За замовчуванням видаляють whitespace, або конкретний набір символів:
" hello ".strip() # "hello"
"--id--".strip("-") # "id"Коротко:
strip-сімейство не змінює рядок in-place, а повертає новий.- Різниця лише у стороні очищення.
- Часто використовується на вхідних даних.
35. Опишіть призначення методу `count` у рядках. Як він працює?
str.count(sub[, start[, end]]) рахує кількість неперекривних входжень
підрядка sub у вибраному діапазоні.
"banana".count("an") # 2
"banana".count("a", 2) # 2Повертає 0, якщо входжень немає.
Коротко:
countшвидко дає кількість входжень підрядка.- Підтримує обмеження пошуку через
start/end. - Рахує неперекривні входження.
36. Як працює метод `join` у Python і для чого найчастіше його використовують?
sep.join(iterable) з'єднує послідовність рядків в один рядок через розділювач
sep.
names = ["Ada", "Linus", "Guido"]
result = ", ".join(names) # "Ada, Linus, Guido"Типове застосування: формування CSV-подібних рядків, логів, SQL-фрагментів, URL-шляхів, повідомлень.
Коротко:
joinце стандартний і швидкий спосіб склеювання рядків.- Викликається на розділювачі, не на списку.
- Ефективніший за багаторазове
+=у циклі.
37. Що таке slicing (нарізка) у Python і як з її допомогою отримати підрядки? Наведіть приклади з позитивними та негативними індексами.
Slicing це вибір частини послідовності через [start:stop:step].
s = "python"
s[1:4] # "yth"
s[:2] # "py"
s[-3:] # "hon"
s[::-1] # "nohtyp"start включається, stop не включається.
Коротко:
- Slicing працює для рядків, списків, кортежів.
- Негативні індекси рахуються з кінця.
- Це базовий інструмент обробки послідовностей без циклів.
38. Поясніть метод `replace` для рядків. Як ним можна замінити декілька входжень підрядка?
str.replace(old, new, count=-1) повертає новий рядок, де old замінено на
new. За замовчуванням замінюються всі входження.
"foo bar foo".replace("foo", "baz") # "baz bar baz"
"foo bar foo".replace("foo", "baz", 1) # "baz bar foo"Коротко:
replaceне змінює рядок in-place.- Без
countзамінює всі входження. countдозволяє контролювати кількість замін.
39. Як працює метод `split` у рядках?
split(sep=None, maxsplit=-1) ділить рядок на список підрядків.
- без
sepділить по будь-яких пробільних символах; - з
sepділить по конкретному розділювачу; maxsplitобмежує кількість розділень.
"a,b,c".split(",") # ["a", "b", "c"]
"a b c".split() # ["a", "b", "c"]
"k=v=x".split("=", 1) # ["k", "v=x"]Коротко:
splitперетворює рядок у список токенів.sep=Noneмає спеціальну поведінку для whitespace.maxsplitкорисний для парсингу key/value форматів.
40. Як працює приведення типів (type casting)?
Type casting це явне перетворення значення між типами через конструктори:
int(), float(), str(), bool(), list(), tuple(), set(), dict().
age = int("42")
price = float("19.99")
text = str(10)Некоректні дані спричиняють виняток (ValueError, TypeError), тому для
зовнішнього вводу потрібна валідація.
Коротко:
- Python надає явні функції конверсії типів.
- Не всі значення можна безпечно конвертувати.
- Для user input потрібні перевірки й обробка винятків.
41. Як Python автоматично конвертує різні типи даних у значення boolean?
У булевому контексті Python застосовує truthiness правила.
False-подібні значення:
False,None;- нульові числа:
0,0.0,0j; - порожні колекції:
"",[],{},set(),tuple(),range(0).
Усе інше зазвичай True.
bool([]) # False
bool("0") # TrueКоротко:
- Boolean-конверсія базується на truthy/falsy правилах.
- Порожнє і нульове ->
False. - Непорожній рядок
"0"все одноTrue.
42. Поясніть, як перетворити рядок у ціле або дійсне число у Python. Що буде, якщо рядок не можна перетворити у число?
Для перетворення використовуйте int() і float():
count = int("42")
ratio = float("3.14")Якщо рядок невалідний, Python піднімає ValueError.
def parse_int(value: str) -> int | None:
try:
return int(value)
except ValueError:
return NoneКоротко:
int()/float()виконують явну конверсію з рядка.- Невалідний формат ->
ValueError. - Для зовнішніх даних потрібен
try/except.
43. Що робить Python при порівнянні різних типів даних, таких як цілі числа і дійсні, або цілі числа і boolean?
Python дозволяє числове порівняння сумісних numeric-типів.
intіfloatпорівнюються за значенням.boolє підкласомint:False == 0,True == 1.
1 == 1.0 # True
True == 1 # True
False < 1 # TrueДля несумісних типів (наприклад int і str) порівняння порядку (<, >)
викликає TypeError.
Коротко:
int,float,boolмають спільну numeric-семантику.boolповодиться як0/1у порівняннях.- Несумісні типи не порівнюються оператором порядку.
44. Як Python обробляє порівняння між списками та set?
list і set це різні типи з різною семантикою, тому пряме порівняння
порядку між ними не підтримується.
[1, 2] == {1, 2} # False
[1, 2] < {1, 2} # TypeErrorЯкщо треба порівняти склад елементів, нормалізуйте тип:
set([1, 2]) == {1, 2} # TrueКоротко:
listіsetпорівнюються як різні структури даних.==між ними зазвичайFalse.- Для змістовного порівняння спершу приводьте до одного типу.
45. Опишіть використання функції `bool()` у Python.
bool(x) повертає булеве значення x за правилами truthiness.
Типові сценарії:
- явне перетворення значення до
True/False; - читабельні умови;
- побудова фільтрів.
bool("text") # True
bool("") # False
bool(0) # FalseКоротко:
bool()уніфікує перевірку "порожнє/непорожнє".- Спирається на вбудовані правила truthy/falsy.
- Корисний для валідації та умовної логіки.
46. У чому різниця між `list` і `tuple`?
Головна різниця: list mutable, tuple immutable.
Практичні наслідки:
listпідходить для даних, що змінюються;tupleпідходить для фіксованих записів;tupleможна використовувати як ключ словника (якщо елементи хешовані).
coords: tuple[float, float] = (50.45, 30.52)
queue: list[str] = ["task-1", "task-2"]Коротко:
listдля змінних колекцій.tupleдля незмінних структур даних.- Вибір впливає на безпечність API і використання в
dict/set.
47. Як можна створити `tuple`?
Основні способи:
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]` при перевертанні списку?
list.reverse() змінює існуючий список in-place і повертає None.
lst[::-1] створює новий перевернутий список (копію).
values = [1, 2, 3]
values.reverse() # values -> [3, 2, 1]
other = values[::-1] # новий списокКоротко:
reverse()змінює оригінал.[::-1]повертає новий об'єкт.- Вибір залежить від потреби зберегти вихідні дані.
49. Поясніть призначення та використання методу `sort` для списків. Як відсортувати список у спадному порядку?
list.sort() сортує список in-place. Підтримує параметри:
key— функція ключа сортування;reverse=True— спадний порядок.
nums = [5, 1, 7]
nums.sort(reverse=True) # [7, 5, 1]Якщо потрібна нова відсортована копія, використовуйте sorted(iterable).
Коротко:
sort()змінює список на місці.- Для спадання використовуйте
reverse=True. sorted()зручний, коли оригінал треба зберегти.
50. У чому різниця між методом `copy` і прямим присвоєнням одного списку іншому? Чому іноді важливо використовувати `copy`?
Присвоєння копіює лише посилання:
a = [1, 2]
b = a
b.append(3) # a теж змінитьсяa.copy() створює новий (поверхневий) список:
a = [1, 2]
b = a.copy()
b.append(3) # a не змінитьсяДля вкладених структур може знадобитися copy.deepcopy.
Коротко:
=не копіює дані, а ділить один об'єкт між змінними.copy()ізолює зміни на рівні верхнього списку.- Для вкладених об'єктів використовуйте
deepcopy.
51. Що робить метод `pop` у списку? Чим його поведінка відрізняється якщо вказати індекс і якщо ні?
pop() видаляє і повертає елемент зі списку.
pop()без аргументів видаляє останній елемент;pop(i)видаляє елемент за індексомi.
items = ["a", "b", "c"]
last = items.pop() # "c"
first = items.pop(0) # "a"Некоректний індекс викликає IndexError.
Коротко:
popпоєднує видалення і повернення значення.- Без індексу працює з кінцем списку.
- З індексом видаляє конкретну позицію.
52. Як об'єднати два списки у Python використовуючи оператор `+` і метод `extend`? У чому ключова різниця між цими двома способами?
a + b створює новий список, а a.extend(b) змінює список a in-place.
a = [1, 2]
b = [3, 4]
c = a + b # [1, 2, 3, 4], a не змінився
a.extend(b) # a -> [1, 2, 3, 4]Коротко:
+повертає новий об'єкт.extend()мутує існуючий список.- Вибір залежить від того, чи треба зберегти оригінал.
53. Для чого застосовуються функції `min`, `max`, `sum`, `all`, `any` у контексті списків?
Це базові агрегатори для ітерованих колекцій:
min()/max()— мінімум і максимум;sum()— сума чисел;all()—True, якщо всі елементи truthy;any()—True, якщо хоча б один елемент truthy.
nums = [3, 10, 1]
min(nums), max(nums), sum(nums) # (1, 10, 14)
all([True, 1, "x"]) # True
any([0, "", None, 5]) # TrueКоротко:
- Функції зменшують boilerplate у циклах.
- Працюють з будь-яким iterable.
- Добре поєднуються з генераторами для lazy-обробки.
54. Що таке словник (`dict`) у Python і чим він відрізняється від інших структур даних?
dict це асоціативний масив: зберігає пари ключ -> значення.
Ключі мають бути hashable, значення можуть бути будь-якого типу.
Відмінності:
- доступ за ключем, а не за індексом;
- середня складність пошуку/вставки/видалення близька до
O(1); - з Python 3.7+ зберігає порядок вставки ключів.
Коротко:
dictоптимальний для швидкого доступу за ключем.- Це основна структура для конфігурацій і мапінгів.
- Ключі повинні бути незмінними (hashable).
55. У чому різниця між отриманням значення зі словника через квадратні дужки `[]` і методом `get`?
d[key] повертає значення або кидає KeyError, якщо ключа немає.
d.get(key, default) повертає значення або default (або None), без винятку.
config = {"timeout": 30}
config["timeout"] # 30
config.get("retries", 3) # 3Коротко:
[]для обов'язкових ключів.get()для опціональних полів.get()зменшує кількістьtry/exceptу читанні даних.
56. Опишіть, як можна оновити та видалити елементи зі словника.
Оновлення:
d[key] = value— вставити/оновити один ключ;d.update({...})— масове оновлення.
Видалення:
del d[key]— видалити ключ (помилка, якщо нема);d.pop(key, default)— видалити і повернути значення;d.popitem()— видалити останню пару;d.clear()— очистити весь словник.
Коротко:
- Для upsert підходять
[]іupdate(). - Для безпечного видалення частіше використовують
pop(). - Обирайте API залежно від бажаної поведінки при відсутньому ключі.
57. Для чого призначені методи `keys`, `values` та `items` у словниках?
Ці методи повертають view-об'єкти словника:
keys()— всі ключі;values()— всі значення;items()— пари(key, value).
data = {"a": 1, "b": 2}
for key, value in data.items():
...View динамічні: відображають актуальний стан словника.
Коротко:
keys/values/itemsпотрібні для ітерації та перевірок.items()найзручніший для циклу з ключем і значенням.- Це не копії, а "живі" представлення даних.
58. Як влаштований `dict` у Python під капотом?
dict реалізований як hash table з оптимізаціями пам'яті і швидкого доступу.
Ключ хешується, за хешем обирається комірка, колізії розв'язуються внутрішнім
алгоритмом probing.
Практичні наслідки:
- середній доступ близько
O(1); - якість
__hash__і__eq__впливає на поведінку; - mutable об'єкти не можна використовувати як ключі.
Коротко:
dictце високопродуктивна hash-структура.- Швидкість досягається за рахунок хешування.
- Ключі мають бути hashable та стабільними.
59. Що таке hash function?
Hash function перетворює об'єкт у ціле число (hash), яке використовується для
швидкого розміщення/пошуку в dict і set.
Умови для коректності:
- якщо
a == b, тодіhash(a) == hash(b); - значення хеша має бути стабільним протягом життя об'єкта.
Коротко:
- Хеш-функція є основою швидкої роботи
dict/set. - Вона не гарантує унікальність (можливі колізії).
- Для custom-класів важлива узгодженість
__eq__і__hash__.
60. Що таке `set` у Python і чим він відрізняється від інших структур даних?
set це невпорядкована колекція унікальних елементів.
Відмінності:
- автоматично прибирає дублікати;
- швидкі операції перевірки належності (
x in s); - підтримує теоретико-множинні операції: union/intersection/difference.
Коротко:
setоптимальний для унікальності та membership-check.- Порядок елементів не гарантується.
- Елементи мають бути hashable.
61. Як створити `set` у Python і які обмеження існують щодо елементів set?
Створення:
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()`?
intersection() повертає спільні елементи множин, а union() об'єднує всі
унікальні елементи з обох множин.
a = {1, 2, 3}
b = {3, 4}
a.intersection(b) # {3}
a.union(b) # {1, 2, 3, 4}Коротко:
intersection= перетин (спільне).union= об'єднання (все унікальне).- Обидва методи базові для порівняння наборів даних.
63. Які типи даних є immutable?
Поширені immutable-типи:
int,float,bool,complex;str,bytes;tuple(якщо елементи теж immutable);frozenset;NoneType.
Коротко:
- Immutable-об'єкт не можна змінити після створення.
- Замість мутації створюється новий об'єкт.
- Такі типи безпечніші для ключів
dict/елементівset.
64. Які типи даних є mutable?
Поширені mutable-типи:
list;dict;set;bytearray;- більшість user-defined об'єктів класів.
Коротко:
- Mutable-об'єкти змінюються in-place.
- Мутації можуть створювати побічні ефекти через спільні посилання.
- Потрібно обережно працювати з копіюванням.
65. Чому важливо розуміти змінюваність, працюючи зі словниками або set?
dict і set базуються на хешуванні, тому їх елементи/ключі мають бути
стабільними (hashable). Mutable-об'єкти не можна безпечно використовувати як
ключі або елементи set.
Також мутація значень через спільні reference часто породжує неочікувані баги.
Коротко:
- Mutability впливає на коректність ключів у
dict/set. - Спільні mutable-об'єкти часто дають побічні ефекти.
- Явні копії і контроль мутацій зменшують баги.
66. Що таке `None`?
None це спеціальне singleton-значення типу NoneType, яке означає
"відсутність значення".
Типові сценарії:
- функція нічого явно не повертає;
- опціональні параметри;
- маркер "дані ще не задані".
Перевірка виконується через is:
if value is None:
...Коротко:
Noneозначає відсутність значення.- Це singleton, тому порівнюють через
is. - Часто використовується в API як опціональний стан.
67. Що таке `id` у Python, як його використовувати і чому це важливо?
id(obj) повертає ідентифікатор об'єкта (унікальний в межах життєвого циклу
об'єкта в поточному процесі).
Корисно для діагностики:
- чи це той самий об'єкт;
- чи створено копію;
- чи сталася мутація спільного reference.
Коротко:
idдопомагає аналізувати identity об'єктів.- Корисний у дебазі копіювання/мутацій.
- Не використовується як бізнес-ідентифікатор.
68. Яка різниця між операторами `is` та `==`?
== порівнює значення (еквівалентність), а is порівнює ідентичність
(чи це один і той самий об'єкт у пам'яті).
a = [1, 2]
b = [1, 2]
a == b # True
a is b # FalseДля None завжди використовуйте is.
Коротко:
==про рівність значень.isпро той самий об'єкт.value is Noneце правильний стиль перевірки.
69. Як застосовується Singleton pattern у Python? Наведіть приклади Singleton-об'єктів у Python.
Singleton означає один глобальний екземпляр об'єкта в процесі. У Python його часто замінюють модульним рівнем (модулі імпортуються один раз).
Приклади singleton-об'єктів мови:
None;TrueіFalse;Ellipsis.
У прикладному коді замість жорсткого Singleton частіше використовують DI-контейнер або фабрики для кращої тестованості.
Коротко:
- Python вже має вбудовані singleton-об'єкти.
- Часто достатньо модульного скоупу без окремого патерна.
- Зловживання Singleton погіршує тестованість.
70. Для чого використовуються оператори `break` та `continue` в циклах Python?
break достроково завершує цикл, continue пропускає поточну ітерацію і
переходить до наступної.
for n in range(10):
if n == 5:
break
if n % 2 == 0:
continueКоротко:
breakзупиняє цикл повністю.continueпропускає лише поточний крок.- Вони роблять керування циклом явним і читабельним.
71. Поясніть поняття нескінченного циклу. У яких випадках доречно використовувати нескінченний цикл і як забезпечити його коректне завершення?
Нескінченний цикл це цикл без природної умови завершення, наприклад while True.
Використовується для daemon/worker-процесів, polling, event-loop логіки.
Коректне завершення:
- чітка умова
break; - обробка сигналів завершення;
- таймаути й
try/finallyдля clean-up.
while True:
task = queue.get()
if task is None:
break
handle(task)Коротко:
while Trueдоречний для довгоживучих сервісних циклів.- Потрібен контрольований механізм зупинки.
- Завжди передбачайте вивільнення ресурсів.
72. Опишіть, як працюють вкладені цикли у Python. Які проблеми з продуктивністю можуть виникати при їх використанні і як їх уникати?
Вкладений цикл це цикл усередині іншого циклу. Часто складність стає O(n*m)
або гірше, що критично на великих даних.
Оптимізації:
- замінювати пошук у списках на
set/dict; - виносити інваріанти за межі внутрішнього циклу;
- застосовувати генератори,
itertools, векторизацію; - профілювати "гарячі" ділянки.
Коротко:
- Вкладені цикли швидко множать вартість обчислень.
- Структури даних часто важливіші за мікрооптимізації.
- Профілювання показує, що саме треба оптимізувати.
73. Що таке structural pattern matching (`match`/`case`)?
match/case (Python 3.10+) це механізм розбору структури даних за шаблонами.
Працює з літералами, типами, послідовностями, словниками та класами.
def handle(message: dict[str, object]) -> str:
match message:
case {"type": "ping"}:
return "pong"
case {"type": "user", "id": int(user_id)}:
return f"user:{user_id}"
case _:
return "unknown"Коротко:
match/caseчитається краще для складного розгалуження.- Дозволяє одночасно перевіряти форму і виймати дані.
- Особливо корисний для протоколів/подій і парсингу структур.
74. У яких випадках pattern matching кращий за `if`/`elif`?
match/case кращий, коли потрібно:
- перевіряти багато взаємовиключних форм даних;
- розпаковувати вкладені структури;
- уникати довгих ланцюгів
if/elif.
if/elif кращий для простих булевих умов і короткої логіки.
Коротко:
- Для структурних сценаріїв краще
match/case. - Для простих умов достатньо
if/elif. - Критерій вибору: читабельність і підтримка.
75. Що таке функція в Python?
Функція це іменований callable-блок коду, який приймає аргументи, повертає результат і дозволяє інкапсулювати логіку.
def normalize_name(name: str) -> str:
return name.strip().title()Функції підтримують default-значення, keyword-аргументи, *args/**kwargs,
анотації типів, декоратори.
Коротко:
- Функція це базова одиниця повторного використання коду.
- Вона формує чіткий контракт через параметри й return.
- Type hints роблять контракт явним.
76. Які існують типи аргументів функцій?
У сучасному Python:
- positional-only (
/); - positional-or-keyword;
- keyword-only (
*); - variadic positional (
*args); - variadic keyword (
**kwargs).
def f(a, /, b, *, c, **kwargs):
...Коротко:
- Python дає гнучкий контроль способу виклику функції.
/і*формалізують API-контракт.*args/**kwargsкорисні для розширюваних інтерфейсів.
77. Що таке позиційні та іменовані аргументи?
Позиційні аргументи передаються за порядком, іменовані (keyword) за назвою
параметра.
def connect(host: str, port: int) -> str:
return f"{host}:{port}"
connect("localhost", 5432) # позиційно
connect(host="localhost", port=5432) # іменованоКоротко:
- Позиційні залежать від порядку параметрів.
- Іменовані підвищують читабельність виклику.
- Їх можна комбінувати з дотриманням правил сигнатури.
78. Що таке аргументи за замовчуванням і які з ними можуть бути проблеми?
Default-аргументи підставляються, якщо значення не передано під час виклику. Вони обчислюються один раз при визначенні функції.
Проблема: mutable default.
def add_item(item: int, bucket: list[int] | None = None) -> list[int]:
if bucket is None:
bucket = []
bucket.append(item)
return bucketКоротко:
- Defaults зручні для стабільних значень.
- Mutable default може накопичувати стан між викликами.
- Безпечний патерн:
None+ ініціалізація всередині.
79. Поясніть призначення та використання `*args` і `**kwargs` у функціях Python. Чим вони відрізняються?
*args збирає додаткові позиційні аргументи в tuple.
**kwargs збирає додаткові іменовані аргументи в dict.
def log_event(event: str, *args: object, **kwargs: object) -> None:
...Використання: обгортки, адаптери API, декоратори, проксування параметрів.
Коротко:
*args= додаткові позиційні.**kwargs= додаткові іменовані.- Вони роблять функції гнучкими, але потребують чіткої валідації.
80. Як визначити функцію з type annotations у Python? Наведіть приклад і поясніть переваги цього підходу.
Типи задаються в сигнатурі параметрів і return-значення.
def process_users(users: list[dict[str, object]]) -> list[str]:
return [str(user["name"]) for user in users if bool(user.get("active"))]Переваги:
- кращий DX (autocomplete, навігація);
- статична перевірка в CI;
- явний контракт для інших розробників.
Коротко:
- Анотації типів документують API.
- Вони зменшують ризик помилок інтеграції.
- Найбільша користь у середніх і великих кодових базах.
81. Що таке lambda-функції?
lambda це анонімна одно-виразна функція.
Зазвичай використовується для коротких callbacks у sorted, map, filter.
users = [{"name": "Ada"}, {"name": "Bob"}]
users_sorted = sorted(users, key=lambda u: u["name"])Для складної логіки краще звичайна def-функція.
Коротко:
lambdaзручна для коротких локальних виразів.- Обмежена одним виразом.
- Для читабельності складний код краще виносити в
def.
82. Яка область видимості змінних у функції?
Python використовує правило LEGB для пошуку імен:
Local;Enclosing (зовнішні функції);Global (модуль);Builtins.
Усередині функції присвоєння створює локальну змінну, якщо не оголошено
global або nonlocal.
Коротко:
- Scope визначає, де ім'я доступне і змінюване.
- LEGB пояснює порядок пошуку змінних.
- Неправильне розуміння scope часто дає
UnboundLocalError.
83. Що таке локальні та глобальні змінні у Python?
Локальні змінні живуть у тілі функції. Глобальні змінні визначені на рівні модуля.
Для зміни глобальної з функції потрібен global, але це зазвичай варто
уникати через неявні залежності.
Коротко:
- Локальні безпечніші для підтримки й тестування.
- Глобальні спрощують доступ, але ускладнюють контроль стану.
- Краще передавати залежності параметрами.
84. Яка різниця між локальними і nonlocal-змінними у Python?
nonlocal використовується у вкладеній функції для зміни змінної із
найближчого enclosing-scope (не глобального).
def counter() -> callable:
value = 0
def inc() -> int:
nonlocal value
value += 1
return value
return incКоротко:
- Локальна змінна належить поточній функції.
nonlocalзмінює стан зовнішньої функції.- Це ключовий механізм для closure зі станом.
85. Що таке unpacking у Python і як його застосовують при присвоєнні?
Unpacking це розкладання елементів послідовності/структури в окремі змінні.
x, y = (10, 20)
first, *middle, last = [1, 2, 3, 4]Працює також зі словниками в match/case, викликах функцій і циклах.
Коротко:
- Unpacking робить код компактнішим і читабельнішим.
- Підтримує "зірочковий" збір решти значень.
- Часто використовується в парсингу структур даних.
86. Поясніть поняття packing значень у Python і наведіть приклади.
Packing це збирання кількох значень в одну структуру (tuple, list, dict).
point = 10, 20 # tuple packing
def collect(*args: int) -> tuple[int, ...]:
return args*args і **kwargs це типовий приклад packing аргументів.
Коротко:
- Packing збирає багато значень в один контейнер.
- Найчастіше використовується в сигнатурах функцій.
- Добре поєднується з unpacking на стороні виклику.
87. Для чого використовують оператор `*` при packing та unpacking?
* у параметрах функції пакує позиційні аргументи (*args), а у виклику
розпаковує iterable в позиційні аргументи.
def add(a: int, b: int) -> int:
return a + b
nums = [2, 3]
add(*nums) # 5Також * використовується у присвоєнні для захоплення "решти" елементів.
Коротко:
*універсальний оператор для роботи з varargs.- У сигнатурі пакує, у виклику розпаковує.
- Зменшує шаблонний код при передачі даних.
88. Що таке closure і яке його відношення до decorator-ів?
Closure це внутрішня функція, яка "пам'ятає" змінні enclosing-scope навіть після завершення зовнішньої функції.
Decorator зазвичай реалізують саме через closure: обгортка зберігає посилання на оригінальну функцію і додаткові параметри.
Коротко:
- Closure це функція + захоплений контекст.
- Decorator часто є практичним застосуванням closure.
- Дає можливість додати поведінку без зміни тіла функції.
89. Що таке decorator у Python і як він працює?
Decorator це callable, який приймає функцію/клас і повертає модифіковану версію (обгортку).
from functools import wraps
def log_calls(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapperЗастосування: логування, кешування, авторизація, ретраї, метрики.
Коротко:
- Decorator додає cross-cutting поведінку.
- Працює через обгортання callable.
- Дозволяє не дублювати технічну логіку в бізнес-коді.
90. Чи можна використовувати декілька decorator-ів для однієї функції?
Так, можна стекувати кілька декораторів.
Вони застосовуються знизу вгору (ближчий до def виконується першим обгортанням).
@decorator_a
@decorator_b
def handler() -> None:
...Еквівалент: handler = decorator_a(decorator_b(handler)).
Коротко:
- Кілька декораторів допустимі і поширені.
- Порядок застосування має значення.
- Стек декораторів треба документувати для прозорості.
91. Опишіть можливу проблему з черговістю decorator-ів при їх застосуванні до функції.
Неправильний порядок може змінити семантику: наприклад, кешування до перевірки доступу може закешувати небажаний результат або обійти очікувану логіку.
Типові ризики:
- логування бачить вже змінені аргументи;
- ретраї обгортають не той виняток;
- кеш ставиться до/після валідації в неправильній точці.
Коротко:
- Порядок декораторів впливає на поведінку функції.
- Це часта причина прихованих багів.
- Для критичних ланцюжків потрібні тести на порядок виконання.
92. Чи можна створити decorator за допомогою класу?
Так. Клас-декоратор реалізує __call__, щоб екземпляр поводився як функція.
class CallCounter:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
return self.func(*args, **kwargs)Коротко:
- Decorator можна реалізувати не лише функцією, а й класом.
- Клас зручний, коли потрібен внутрішній стан.
- Ключовий механізм:
__call__.
93. Як визначити decorator, що приймає параметри?
Потрібна трирівнева структура: фабрика декоратора -> декоратор -> wrapper.
from functools import wraps
def retry(times: int):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
for _ in range(times - 1):
try:
return func(*args, **kwargs)
except Exception:
pass
return func(*args, **kwargs)
return wrapper
return decoratorКоротко:
- Параметризований декоратор це функція, що повертає декоратор.
- Часто використовують closure для збереження параметрів.
- Такий підхід зручний для конфігурованої поведінки.
94. Для чого використовують `functools.wraps` у decorator-функціях?
functools.wraps копіює метадані оригінальної функції в wrapper:
ім'я, docstring, модуль, а також __wrapped__.
Це важливо для:
- коректного дебагу й логів;
- інтроспекції та документації;
- сумісності з інструментами, що читають сигнатуру.
Коротко:
wrapsзберігає "ідентичність" оригінальної функції.- Без нього декоровані функції втрачають корисні метадані.
- Це best practice для всіх wrapper-декораторів.
95. Як працюють dict comprehension, list comprehension і set comprehension?
Comprehension створює колекцію з виразу і циклу(ів), опційно з фільтром.
squares = [x * x for x in range(6)]
mapping = {x: x * x for x in range(6)}
unique = {x % 3 for x in range(10)}Формат:
- list:
[expr for x in it if cond] - set:
{expr for x in it if cond} - dict:
{k_expr: v_expr for x in it if cond}
Коротко:
- Comprehension компактно виражає трансформацію даних.
- Працює для
list,set,dict. - Дає чистіший код, ніж ручне додавання в циклі.
96. Які переваги використання list comprehensions у порівнянні зі звичайними циклами?
Переваги:
- коротший і декларативніший код;
- менше службових змінних;
- зазвичай трохи краща продуктивність у CPython;
- нижчий ризик забути
append.
Недолік: для дуже складної логіки comprehension погіршує читабельність.
Коротко:
- List comprehension добре підходить для простих трансформацій.
- Часто швидший і чистіший за ручний цикл.
- Для складних гілок краще звичайний
for.
97. Чи можете навести приклад вкладеної list comprehension?
Приклад flatten матриці:
matrix = [[1, 2], [3, 4], [5, 6]]
flat = [item for row in matrix for item in row] # [1, 2, 3, 4, 5, 6]Приклад з умовою:
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]Коротко:
- Вкладена comprehension це кілька
forв одному виразі. - Порядок
forвідповідає вкладеним циклам. - Використовуйте, коли вираз залишається читабельним.
98. Що швидше у Python: list comprehension або створення списку за допомогою циклу?
У типових сценаріях list comprehension трохи швидший за цикл з append,
бо має оптимізований байткод і менше накладних операцій.
Важливо: реальний приріст залежить від тіла операції, тому для критичних
ділянок варто вимірювати (timeit, pyperf).
Коротко:
- Часто швидше: list comprehension.
- Різниця може бути невеликою.
- Для production-рішень орієнтуйтесь на вимірювання.
99. Що таке iterator у Python?
Iterator це об'єкт, який повертає елементи послідовно і пам'ятає поточний стан. Він реалізує протокол:
__iter__()повертає себе;__next__()повертає наступний елемент або кидаєStopIteration.
Коротко:
- Iterator дає поелементний доступ без повного завантаження даних.
- Це база для
for, генераторів і lazy-обробки. - Після вичерпання iterator не "перемотується" сам.
100. Як створити iterator з ітерованого об'єкта за допомогою функції `iter()`?
Викликайте iter(iterable), щоб отримати iterator.
items = [10, 20, 30]
it = iter(items)Після цього значення читаються через next(it).
Коротко:
iter()перетворює iterable на iterator.- Це явний спосіб керувати ітерацією вручну.
- Використовується у низькорівневій обробці потоку даних.
101. Для чого призначена функція `next()` при роботі з iterator-ами?
next(iterator[, default]) повертає наступний елемент iterator.
Коли елементи закінчились, кидає StopIteration або повертає default, якщо
його передано.
it = iter([1, 2])
next(it) # 1
next(it) # 2
next(it, None) # NoneКоротко:
next()дає ручний контроль кроку ітерації.- Без
defaultкінець потоку ->StopIteration. - З
defaultзручно безпечно читати потік.
102. Чи можна взаємозамінно використовувати методи `__next__()` і `__iter__()` з функціями `next()` та `iter()`?
Так, але з нюансом протоколу:
iter(obj)викликаєobj.__iter__();next(it)викликаєit.__next__().
Тобто функції є стандартним інтерфейсом до dunder-методів і зазвичай використовуються замість прямого виклику.
Коротко:
iter()відповідає__iter__().next()відповідає__next__().- У прикладному коді краще викликати вбудовані функції.
103. Яка роль `StopIteration` у проєктуванні iterator-ів і коли це зазвичай виникає?
StopIteration сигналізує, що iterator вичерпано.
for-цикл перехоплює його автоматично і завершує ітерацію.
Зазвичай виникає:
- при виклику
next(it)після останнього елемента; - у custom-ітераторах, коли дані закінчились.
Коротко:
StopIterationце штатний кінець потоку.- Його не треба логувати як "помилку" в normal-flow.
- У власних iterator-ах його треба піднімати коректно.
104. Що таке generator і чим він відрізняється від iterator чи звичайної функції?
Generator це спеціальний iterator, який створюється функцією з yield.
Він генерує значення по одному і зберігає внутрішній стан між викликами.
Відмінності:
- від звичайної функції: не завершується одним
return, а "пауза/продовження"; - від ручного iterator: простіша реалізація без явного класу
__next__.
Коротко:
- Generator це найзручніший спосіб lazy-ітерації.
- Дає менше коду, ніж custom iterator-клас.
- Особливо корисний для великих потоків даних.
105. Як створити generator-функцію?
Потрібно визначити функцію з yield.
def countdown(start: int):
current = start
while current > 0:
yield current
current -= 1Виклик countdown(3) повертає generator-об'єкт, який можна ітерувати.
Коротко:
- Наявність
yieldробить функцію генератором. - Generator повертає значення поетапно.
- Стан функції зберігається між ітераціями.
106. Як ключове слово `yield` забезпечує функціональність generator-ів і чому вони економлять пам'ять?
yield повертає чергове значення і "заморожує" контекст функції (локальні
змінні, позицію виконання). Наступний next() відновлює виконання з цієї точки.
Економія пам'яті: дані не створюються повністю наперед, а обчислюються on-demand.
Коротко:
yieldреалізує паузу/продовження виконання.- Generator підтримує lazy evaluation.
- Це знижує memory footprint для великих наборів даних.
107. У чому різниця між `return` і `yield`?
return завершує функцію і повертає одне фінальне значення.
yield повертає проміжне значення і зберігає стан для подальшого продовження.
У генераторі return означає кінець ітерації (StopIteration).
Коротко:
return-> завершення функції.yield-> поетапна видача значень.yieldвикористовується для потокової обробки.
108. Що таке Object-Oriented Programming (OOP) та його головні принципи у Python?
OOP це підхід, де дані і поведінка об'єднані в об'єктах.
Ключові принципи:
- інкапсуляція;
- наслідування;
- поліморфізм;
- абстракція.
У Python OOP зазвичай поєднується з композицією і duck typing.
Коротко:
- OOP структурує домен через класи й об'єкти.
- Python підтримує OOP гнучко, без надмірного шаблонного коду.
- На практиці композиція часто краща за глибоке наслідування.
109. Що таке `class` у Python?
class це шаблон (blueprint) для створення об'єктів з атрибутами та методами.
class User:
def __init__(self, name: str) -> None:
self.name = nameКлас задає структуру і поведінку майбутніх екземплярів.
Коротко:
- Клас описує дані й операції над ними.
- На основі класу створюються об'єкти (instances).
- Це базова одиниця моделювання в OOP.
110. Як створити object у Python?
Об'єкт створюється викликом класу:
class User:
def __init__(self, name: str) -> None:
self.name = name
user = User("Ada")Під час створення виконується __init__ для ініціалізації стану.
Коротко:
- Object = екземпляр класу.
- Створення:
instance = ClassName(...). __init__налаштовує початкові атрибути.
111. Що таке об'єкти та атрибути?
Об'єкт це конкретний екземпляр типу/класу. Атрибут це пов'язане з об'єктом ім'я-значення (дані або метод).
user.name # атрибут-дані
user.save() # атрибут-методКоротко:
- Об'єкт зберігає стан і поведінку.
- Атрибути описують цей стан/поведінку.
- Доступ до атрибутів: крапкова нотація.
112. Яку роль виконує метод `__init__()` у класі?
__init__ це ініціалізатор екземпляра: викликається після створення об'єкта і
заповнює початковий стан.
class Account:
def __init__(self, owner: str, balance: float = 0.0) -> None:
self.owner = owner
self.balance = balanceКоротко:
__init__задає стартові атрибути об'єкта.- Працює як вхідна точка конфігурації instance.
- Не створює об'єкт, а лише ініціалізує його.
113. Для чого параметр `self` у методах класу Python?
self це посилання на поточний екземпляр, через нього метод читає/змінює
інстанс-атрибути.
def deposit(self, amount: float) -> None:
self.balance += amountІм'я self не зарезервоване синтаксично, але є загальноприйнятим стандартом.
Коротко:
selfзв'язує метод з конкретним об'єктом.- Через
selfдоступні інстанс-дані. - Це обов'язковий перший параметр instance method.
114. Як визначати методи у класі?
Методи визначаються як функції всередині class.
class Calculator:
def add(self, a: int, b: int) -> int:
return a + bТипові види: instance (self), class (@classmethod, cls), static
(@staticmethod, без self/cls).
Коротко:
- Метод це функція в тілі класу.
- Тип методу визначається декоратором і сигнатурою.
- Найчастіше використовується instance method.
115. Поясніть концепцію "все є об'єктом" у Python і наведіть приклади.
У Python майже все є об'єктом: числа, рядки, функції, класи, модулі. Тому у всього є тип, атрибути й поведінка.
type(10) # <class 'int'>
type(len) # <class 'builtin_function_or_method'>
type(str) # <class 'type'>Коротко:
- Єдина об'єктна модель спрощує мову.
- Функції й класи теж об'єкти першого класу.
- Це робить Python гнучким для метапрограмування.
116. Наведіть приклад класу з методами, які виконують обчислення на основі атрибутів.
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-програмі.
Коли домен має кілька відповідальностей, їх розділяють по класах.
Наприклад e-commerce: Order, OrderItem, PaymentService, InventoryService.
Це дає:
- чіткий розподіл відповідальностей;
- слабше зв'язування;
- простішу заміну/тестування компонентів.
Коротко:
- Кілька класів потрібні для моделювання складного домену.
- Розподіл відповідальностей підвищує підтримуваність.
- Композиція класів зазвичай краща за "god object".
118. У чому різниця між instance method, class method і static method?
- Instance method: має
self, працює з конкретним екземпляром. - Class method: має
cls, працює з класом загалом. - Static method: не має
self/cls, це утилітарна функція в namespace класу.
Коротко:
- Instance -> логіка екземпляра.
- Class -> логіка класу/альтернативні конструктори.
- Static -> допоміжна логіка без доступу до стану.
119. Що таке `@classmethod` у Python і чим він відрізняється від звичайних методів?
@classmethod передає першим аргументом клас (cls), а не екземпляр.
Часто використовується для factory-методів.
class User:
def __init__(self, name: str) -> None:
self.name = name
@classmethod
def from_email(cls, email: str) -> "User":
return cls(email.split("@")[0])Коротко:
classmethodпрацює на рівні класу.- Зручний для альтернативних конструкторів.
- Не потребує вже створеного instance.
120. Що таке `@staticmethod` у класах Python і коли його доцільно застосовувати?
@staticmethod визначає метод без автоматичного self або cls.
Він логічно належить класу, але не залежить від стану.
class Math:
@staticmethod
def clamp(value: int, min_v: int, max_v: int) -> int:
return max(min_v, min(value, max_v))Коротко:
staticmethodце функція в namespace класу.- Використовуйте для допоміжної логіки без доступу до атрибутів.
- Не підходить, якщо потрібен стан instance або class.
121. У чому різниця між `@classmethod` і `@staticmethod` у класах Python?
Різниця у першому аргументі і рівні доступу:
@classmethodотримуєclsі може працювати з класовим станом;@staticmethodне отримує нічого автоматично.
classmethod частіше для фабрик/поліморфних конструкторів,
staticmethod для утиліт.
Коротко:
classmethodзнає про клас.staticmethodізольований від класового/інстанс стану.- Вибір залежить від потреби доступу до
cls.
122. Що таке інстанс-атрибути у класах Python і чим вони відрізняються від класових атрибутів?
Інстанс-атрибути належать конкретному об'єкту (self.x).
Класові атрибути належать класу і спільні для всіх екземплярів.
class User:
role = "member" # class attribute
def __init__(self, name: str) -> None:
self.name = name # instance attributeКоротко:
- Інстанс-атрибути зберігають індивідуальний стан.
- Класові атрибути зберігають спільну конфігурацію.
- Мутації class-атрибутів впливають на всі instance.
123. Що таке `__slots__` у Python?
__slots__ обмежує набір дозволених атрибутів у класі і може зменшити
споживання пам'яті, прибираючи __dict__ для екземплярів.
class Point:
__slots__ = ("x", "y")
def __init__(self, x: int, y: int) -> None:
self.x = x
self.y = yКоротко:
__slots__корисний для великої кількості легковагових об'єктів.- Дає memory-оптимізацію і контроль атрибутів.
- Зменшує гнучкість динамічного додавання полів.
124. Що таке magic methods (dunder методи) у класах Python і чому їх називають "магічними"?
Dunder-методи (__init__, __str__, __len__, __eq__, ...) це спеціальні
хуки, які Python викликає автоматично у відповідь на оператори та built-ins.
Їх називають "магічними", бо вони інтегрують ваш клас у поведінку мови.
Коротко:
- Dunder-методи визначають протокольну поведінку об'єкта.
- Вони дозволяють вашим класам поводитись "як вбудовані типи".
- Використовуйте їх лише коли є чітка семантична потреба.
125. Для чого призначений метод `__del__`?
__del__ це фіналізатор, який може викликатися перед знищенням об'єкта GC.
Його поведінка не детермінована, тому для керування ресурсами краще
використовувати context managers (with) і явне закриття.
Коротко:
__del__не гарантує своєчасного виконання.- Не покладайте критичний clean-up лише на нього.
- Рекомендований підхід:
with/try-finally.
126. Наведіть приклад використання магічного методу `__str__` для визначення текстового представлення власного класу у 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__`?
__str__ орієнтований на кінцевого читача.
__repr__ орієнтований на розробника/debug, бажано щоб був однозначним.
print(obj) переважно використовує __str__,
а REPL/repr(obj) використовує __repr__.
Коротко:
__str__для дружнього виводу.__repr__для технічного представлення.- Добра практика: мати обидва, якщо клас доменний.
128. Як працює operator overloading у Python і чому це корисно?
Operator overloading це реалізація dunder-методів операторів (__add__,
__sub__, __eq__, ...), щоб об'єкти власного класу підтримували оператори.
class Vector2:
def __init__(self, x: float, y: float) -> None:
self.x = x
self.y = y
def __add__(self, other: "Vector2") -> "Vector2":
return Vector2(self.x + other.x, self.y + other.y)Коротко:
- Дає природний синтаксис для доменних типів.
- Покращує виразність API.
- Важливо зберігати математично очікувану семантику.
129. Що таке inheritance (наслідування)?
Наслідування дозволяє створити дочірній клас, який успадковує атрибути й методи базового класу та може розширювати/перевизначати поведінку.
Коротко:
- Наслідування сприяє повторному використанню коду.
- Дочірній клас може перевизначати методи базового.
- Надмірна ієрархія ускладнює підтримку, тому часто краще композиція.
130. Що таке single inheritance у Python?
Single inheritance означає, що клас має лише одного прямого батька.
class Animal:
...
class Dog(Animal):
...Це найпростіша і зазвичай найчитабельніша форма наслідування.
Коротко:
- Один дочірній клас -> один базовий клас.
- Проста модель резолюції методів.
- Часто достатня для більшості бізнес-моделей.
131. Як реалізувати наслідування у класах Python і який для цього використовується синтаксис?
Синтаксис: class Child(Base):.
class Base:
def greet(self) -> str:
return "hello"
class Child(Base):
def greet(self) -> str:
return "hi"За потреби виклику базової логіки використовуйте super().
Коротко:
- Наслідування задається у дужках після імені класу.
- Дочірній клас отримує API базового.
- Перевизначення дозволяє адаптувати поведінку.
132. Як отримати доступ до членів базового класу у дочірньому класі?
Доступ можливий напряму через успадковані атрибути/методи або через super().
class Base:
def greet(self) -> str:
return "hello"
class Child(Base):
def greet(self) -> str:
return super().greet() + " world"Коротко:
- Успадковані члени доступні в дочірньому класі автоматично.
super()коректно викликає логіку базового класу.- Це важливо для розширення, а не дублювання поведінки.
133. Для чого функція `super()` у наслідуванні Python, як її використовують?
super() повертає проксі до наступного класу за MRO, щоб викликати його методи.
Типово використовується в __init__ і при cooperative multiple inheritance.
class Child(Base):
def __init__(self, value: int) -> None:
super().__init__()
self.value = valueКоротко:
super()викликає батьківську реалізацію без жорсткого імені класу.- Допомагає підтримувати MRO-коректність.
- Рекомендований спосіб роботи з наслідуванням.
134. Опишіть функцію `isinstance()` у Python і наведіть приклад її використання.
isinstance(obj, cls_or_tuple) перевіряє, чи об'єкт належить типу або його
підкласу.
isinstance(10, int) # True
isinstance(True, int) # True
isinstance("x", (str, bytes)) # TrueКоротко:
isinstanceбезпечніший заtype(obj) is ...у поліморфному коді.- Враховує ієрархію наслідування.
- Підтримує кортеж типів.
135. Поясніть функцію `issubclass()` у Python і наведіть приклад її застосування.
issubclass(Sub, Base) перевіряє, чи клас Sub є підкласом Base.
class Animal: ...
class Dog(Animal): ...
issubclass(Dog, Animal) # TrueКоротко:
- Працює з класами, не з екземплярами.
- Зручно для валідації API на рівні типів.
- Враховує транзитивне наслідування.
136. Що таке multiple inheritance (множинне наслідування)?
Multiple inheritance це спадкування одного класу від кількох базових класів.
class A: ...
class B: ...
class C(A, B): ...Дає гнучкість, але вимагає дисципліни у дизайні методів і super().
Коротко:
- Один клас може успадковувати поведінку з кількох джерел.
- Корисно для mixin-підходу.
- Може ускладнювати розуміння MRO.
137. Як працює MRO (Method Resolution Order) у multiple inheritance?
MRO визначає порядок пошуку методів у ієрархії класів. У Python використовується алгоритм C3 linearization.
Переглянути порядок можна через ClassName.__mro__ або ClassName.mro().
Коротко:
- MRO вирішує, з якого базового класу брати метод.
- Порядок передбачуваний і формально визначений (C3).
- Для cooperative inheritance всі класи мають викликати
super().
138. Які переваги та недоліки використання multiple inheritance?
Переваги:
- повторне використання поведінки з кількох джерел;
- зручні mixins для "додавання" можливостей.
Недоліки:
- складніший MRO;
- ризик конфліктів імен/поведінки;
- складніший дебаг і онбординг.
Коротко:
- MI потужний, але вимагає строгих правил дизайну.
- Для більшості кейсів композиція простіша.
- Використовуйте MI переважно для невеликих mixins.
139. Що таке Mixins?
Mixin це невеликий клас з вузькою додатковою поведінкою, який призначений для комбінування через наслідування, а не для самостійного використання.
Приклади: TimestampMixin, JsonSerializableMixin.
Коротко:
- Mixin додає одну конкретну можливість.
- Зазвичай не має власного повного життєвого циклу.
- Добре поєднується з multiple inheritance.
140. Що таке encapsulation (інкапсуляція) у Python?
Інкапсуляція це приховування внутрішньої реалізації за стабільним публічним API.
У Python це реалізується переважно конвенціями і property, а не жорсткими
модифікаторами доступу.
Коротко:
- Клієнт коду працює з інтерфейсом, а не з деталями.
- Інкапсуляція зменшує зв'язування між компонентами.
- Полегшує зміну внутрішньої реалізації без ломання API.
141. Яка різниця між доступом public, private і protected?
У 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 часто реалізується duck typing і протоколами.
def render(obj) -> str:
return obj.to_text()Будь-який об'єкт з методом to_text підходить.
Коротко:
- Один інтерфейс, багато реалізацій.
- У Python поліморфізм часто "поведінковий", а не ієрархічний.
- Це спрощує розширення системи новими типами.
143. Що таке abstraction (абстракція) у Python?
Абстракція це виділення суттєвого API і приховування зайвих деталей реалізації. Клієнт працює з контрактом, а не з внутрішніми кроками.
Коротко:
- Абстракція знижує когнітивне навантаження.
- Полегшує заміну реалізації без змін клієнтського коду.
- Реалізується через інтерфейси, ABC, протоколи, фасади.
144. Як реалізувати data abstraction?
Підхід:
- сховати прямий доступ до внутрішніх полів (
_field); - надати контрольований API через методи/
@property; - валідувати інваріанти в setter-логіці.
class Temperature:
def __init__(self, celsius: float) -> None:
self.celsius = celsius
@property
def celsius(self) -> float:
return self._celsius
@celsius.setter
def celsius(self, value: float) -> None:
if value < -273.15:
raise ValueError("invalid temperature")
self._celsius = valueКоротко:
- Data abstraction захищає інваріанти об'єкта.
propertyдає контрольований доступ до стану.- Внутрішні деталі можна змінювати без зміни API.
145. Що таке ABC та `@abstractmethod`?
ABC (Abstract Base Class) це абстрактний базовий клас з модуля abc.
@abstractmethod позначає метод, який обов'язково має реалізувати підклас.
from abc import ABC, abstractmethod
class Storage(ABC):
@abstractmethod
def save(self, data: bytes) -> None:
...Коротко:
- ABC формалізує контракт ієрархії.
@abstractmethodзабороняє інстанціювати неповну реалізацію.- Корисно для плагінних/розширюваних архітектур.
146. Що таке property у Python і як їх застосовують?
property перетворює методи доступу в атрибутоподібний API з можливістю
валідації, обчислень або lazy-логіки.
class User:
def __init__(self, name: str) -> None:
self._name = name
@property
def name(self) -> str:
return self._nameКоротко:
propertyдає контроль доступу без зміни зовнішнього синтаксису.- Підходить для валідації й derived-значень.
- Дозволяє еволюціонувати API без ломання клієнтів.
147. Що таке `@property`?
@property це декоратор для getter-методу.
Разом з @x.setter і @x.deleter формує керований атрибут.
Коротко:
@propertyдозволяє читати метод як поле.- Допомагає інкапсулювати внутрішню реалізацію.
- Часто використовується для backward-compatible API.
148. Що таке descriptor у Python?
Descriptor це об'єкт, який реалізує __get__, __set__ або __delete__ і
керує доступом до атрибутів іншого класу.
Через descriptor працюють property, classmethod, staticmethod.
Коротко:
- Descriptor це низькорівневий механізм атрибутного доступу.
- Дозволяє повторно використовувати логіку валідації/проксування полів.
- Це основа багатьох метапрограмних технік Python.
149. Чим відрізняються property і descriptor?
property це готовий високорівневий descriptor для одного атрибута.
Кастомний descriptor це загальніший механізм, який можна перевикористати в
багатьох полях/класах.
Коротко:
propertyпростіший і локальний.- Descriptor гнучкіший і масштабованіший.
propertyфактично побудований на descriptor-протоколі.
150. Коли доцільніше використовувати property, а коли descriptor?
Використовуйте property, коли логіка стосується одного-двох полів конкретного
класу. Використовуйте descriptor, коли ту саму логіку (валідація, кастинг,
ленива ініціалізація) треба повторно застосувати в багатьох класах.
Коротко:
- Локальна логіка поля ->
property. - Повторне використання політики доступу -> descriptor.
- Descriptor вигідний у великих доменних моделях.
151. Для чого використовують `setattr()`, `getattr()` і `hasattr()`? Яка між ними різниця?
Це функції динамічного доступу до атрибутів:
getattr(obj, name[, default])— прочитати атрибут;setattr(obj, name, value)— встановити атрибут;hasattr(obj, name)— перевірити наявність.
value = getattr(user, "email", None)
if not hasattr(user, "active"):
setattr(user, "active", True)Коротко:
getattr/setattr/hasattrпотрібні для динамічної роботи з об'єктами.- Корисні в generic-коді, serialization, адаптерах.
- Важливо не зловживати, щоб не втратити читабельність.
152. Поясніть значення методу `__set_name__` у Python-descriptor-ах та наведіть приклад його застосування.
__set_name__(self, owner, name) викликається під час створення класу і дає
descriptor-у знати ім'я атрибута, до якого його прив'язано.
class Field:
def __set_name__(self, owner, name): self.name = nameКоротко:
__set_name__ініціалізує descriptor контекстом класу.- Дозволяє робити перевикористовувані валідатори полів.
- Спрацьовує один раз на етапі class creation.
153. Що таке `dataclass` і коли його варто використовувати?
@dataclass автоматично генерує boilerplate (__init__, __repr__, __eq__).
Підходить для моделей даних без складної поведінки.
from dataclasses import dataclass
@dataclass(slots=True)
class User: name: str; active: bool = TrueКоротко:
dataclassскорочує код для data-моделей.- Добрий вибір для DTO/конфігурацій.
- Для складної валідації часто потрібні інші інструменти.
154. У чому різниця між `dataclass` і Pydantic?
dataclass фокусується на зручному описі структури.
Pydantic додає runtime-валідацію, парсинг і серіалізацію даних.
Коротко:
dataclassлегший і швидший для внутрішніх моделей.- Pydantic кращий для зовнішнього вводу/API.
- Вибір залежить від потреби валідації під час виконання.
155. Що таке type hints і навіщо вони потрібні?
Type hints це анотації типів у сигнатурах і змінних, що формують явний контракт. Вони покращують IDE-підказки і статичний аналіз.
Коротко:
- Type hints підвищують зрозумілість API.
- Дають раннє виявлення класу помилок.
- Корисні навіть у динамічній мові.
156. Як працює статична перевірка типів (mypy)?
mypy читає type hints і аналізує код без запуску, перевіряючи сумісність типів.
Він ловить помилки інтеграції до runtime.
Коротко:
mypyвиконує compile-time-подібну перевірку для Python.- Працює найкраще в CI.
- Строгі режими зменшують кількість production-багів.
157. Як забезпечити type safety у Python-проєкті?
- послідовно додати type hints у публічний API;
- увімкнути
mypy/pyrightу CI; - використовувати
TypedDict,Protocol, generics; - мінімізувати
Anyі неявні касти.
Коротко:
- Type safety це процес, а не разова дія.
- Найбільший ефект дає CI-гейт на типи.
- Поступове підвищення strictness працює краще за "big bang".
158. Що таке `TypedDict`?
TypedDict описує типізовану форму словника: які ключі очікуються і яких типів
їх значення.
from typing import TypedDict
class UserPayload(TypedDict): name: str; active: boolКоротко:
TypedDictтипізуєdictз фіксованими ключами.- Зручний для JSON-подібних структур.
- Перевіряється статичним аналізатором.
159. Що таке `Protocol` у typing?
Protocol описує поведінковий контракт (structural typing): тип сумісний, якщо
має потрібні методи/атрибути, незалежно від наслідування.
Коротко:
Protocolреалізує duck typing у статичній типізації.- Зменшує жорстке зв'язування з конкретними класами.
- Корисний для тестованих і розширюваних API.
160. Що таке Generics у Python?
Generics дозволяють писати типи й функції, параметризовані іншими типами.
У сучасному синтаксисі: class Box[T]: ..., def first[T](...) -> T.
Коротко:
- Generics роблять типи повторно використовуваними.
- Підсилюють type safety колекцій і контейнерів.
- Зменшують дублювання типізованого коду.
161. Що таке Pydantic і для чого він використовується?
Pydantic це бібліотека для опису схем даних з runtime-валідацією, перетворенням типів і зручною серіалізацією.
Типові кейси: FastAPI request/response моделі, конфігурація застосунку.
Коротко:
- Pydantic валідовує зовнішні дані під час виконання.
- Зручний для API і інтеграцій.
- Дає чіткі помилки валідації та схеми.
162. Що таке exception-и у Python?
Exception це об'єкт, який сигналізує про помилкову або виняткову ситуацію під час виконання коду.
Коротко:
- Exception-и переривають normal-flow.
- Їх треба обробляти там, де можна прийняти рішення.
- Коректна модель помилок підвищує надійність системи.
163. Які є три типи помилок у Python і чим вони відрізняються?
- Syntax errors: помилки синтаксису до запуску;
- Runtime exceptions: помилки під час виконання;
- Logical errors: код виконується, але результат неправильний.
Коротко:
- Syntax/runtime ловить інтерпретатор.
- Логічні помилки ловляться тестами і рев'ю.
- Кожен тип потребує різного підходу діагностики.
164. Як використовувати `try`, `except`, `else` та `finally`?
try містить ризикову операцію, except обробляє помилку, else виконується
коли помилки не було, finally виконується завжди (clean-up).
try: data = load()
except FileNotFoundError: data = {}
else: validate(data)
finally: close_connections()Коротко:
elseтримайте для коду "успішного шляху".finallyдля ресурсів/очищення.- Ловіть конкретні exception-и, не "все підряд".
165. Що означає порядок `except`-категорій?
except перевіряються зверху вниз, тому спочатку ставлять найконкретніші
винятки, а загальні (Exception) в кінці.
Коротко:
- Порядок
exceptвпливає на те, який обробник спрацює. - Широкий
exceptзверху "з'їдає" специфічні кейси. - Це критично для правильного recovery-flow.
166. Яке призначення ключового слова `assert` у Python-коді?
assert перевіряє інваріант і піднімає AssertionError, якщо умова хибна.
Підходить для внутрішніх перевірок розробника, не для валідації user input.
Коротко:
assertдокументує "це має бути істинним".- Не замінює продакшен-обробку помилок.
- Використовуйте для контрактів у внутрішній логіці.
167. Чим `raise` відрізняється від простого print error message у Python?
raise змінює контроль потоку і сигналізує помилку викликувачеві.
print лише виводить текст і не зупиняє помилковий сценарій.
Коротко:
raiseце механізм обробки помилок,printні.- Exception-и можна централізовано ловити/логувати.
printдля діагностики, не для error-contract.
168. Як створити власний виняток (custom exception)?
Створіть клас, що наслідує Exception (або конкретніший базовий виняток).
class InvalidOrderError(Exception):
passКоротко:
- Custom exception робить помилки доменно-виразними.
- Дозволяє точково ловити потрібні кейси.
- Краще за універсальний
ValueErrorскрізь.
169. Яке значення має створення custom exceptions у Python і як вони покращують обробку помилок?
Власні exception-и формують явну error-модель домену й відділяють технічні помилки від бізнес-правил.
Коротко:
- Покращують читабельність і підтримку обробників.
- Дають точнішу семантику в логах/API.
- Спрощують тестування негативних сценаріїв.
170. Як організовані exception-и у Python і яка ієрархія exception-класів?
Ієрархія побудована від BaseException.
Для прикладного коду майже завжди працюють з нащадками Exception.
Ключові гілки: ValueError, TypeError, KeyError, OSError, RuntimeError.
Коротко:
- Exception-и утворюють дерево класів.
- Краще ловити конкретні підкласи.
BaseExceptionзазвичай не перехоплюють у бізнес-логіці.
171. Які підходи до обробки кількох різних exception-ів у Python і чому їх слід обробляти окремо?
Підходи:
- окремі
exceptдля кожного типу; - групування логічно однакових:
except (A, B):; - окремі recovery-стратегії для кожної категорії.
Коротко:
- Різні exception-и часто потребують різних дій.
- Окрема обробка зменшує приховані дефекти.
- Логи стають точнішими й кориснішими.
172. Для чого потрібно повторно піднімати (re-raise) exception у Python і коли це буває корисно?
Re-raise (raise без аргументів у except) дозволяє додати контекст (лог,
метрики, cleanup) і передати ту саму помилку вище.
Коротко:
- Re-raise зберігає початковий traceback.
- Корисний для централізованої обробки на вищому рівні.
- Не "ковтайте" критичні помилки без потреби.
173. Чому використання try-except блоку варто обмежувати у програмах Python і як це впливає на продуктивність?
try/except потрібен, але не має обгортати великі блоки "про всяк випадок".
Особливо дорого, коли винятки виникають часто (exception-driven flow).
Коротко:
- Ловіть лише очікувані помилки в вузькому місці.
- Часті exception-и погіршують продуктивність і читабельність.
- Prefer explicit checks там, де це доречно.
174. Як обробляти помилки під час роботи з файлами?
Використовуйте with і ловіть конкретні винятки (FileNotFoundError,
PermissionError, UnicodeDecodeError, OSError).
try:
with open(path, "r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
...Коротко:
withгарантує закриття файлу.- Обробляйте конкретні I/O помилки.
- Логуйте контекст (шлях, режим, кодування).
175. Які режими роботи з файлами у Python?
Основні режими:
rчитання;wперезапис;aдозапис в кінець;xстворення нового файлу;bбінарний режим;tтекстовий (за замовчуванням);+читання+запис.
Коротко:
- Режим визначає семантику безпеки й змін файлу.
wзатирає вміст,aзберігає існуючий.- Для байтових даних використовуйте
b.
176. Чому важливо закривати файли після операцій і що може статись, якщо залишити файл відкритим?
Відкритий файл утримує дескриптор ОС. Якщо не закривати:
- витік file descriptors;
- блокування файлів/неповний flush;
- нестабільність на довгих процесах.
Коротко:
- Закриття файлу вивільняє ресурси ОС.
withавтоматизує безпечне закриття.- Це критично для сервісів і batch-скриптів.
177. У чому різниця між методами `read()`, `readline()` та `readlines()` для читання файлів?
read()читає весь файл (абоnбайт/символів);readline()читає один рядок;readlines()читає всі рядки у список.
Коротко:
read()іreadlines()можуть бути важкими по пам'яті.- Для великих файлів краще ітерація
for line in file. - Вибір залежить від обсягу і сценарію обробки.
178. Як у Python записати дані у файл і яка різниця між режимом `w` (write) та `a` (append)?
Запис:
with open("out.txt", "w", encoding="utf-8") as f:
f.write("hello\n")w перезаписує файл з початку, a додає новий контент у кінець.
Коротко:
wзатирає старі дані.aзберігає існуючий вміст.- Для журналів зазвичай використовують
a.
179. Як працювати з великими файлами ефективно?
- читати потоково (по рядках або чанках);
- уникати завантаження всього файлу в пам'ять;
- використовувати буферизацію і генератори;
- для колонкових/табличних даних обирати відповідні формати й парсери.
Коротко:
- Ключ: streaming замість full-read.
- Генератори зменшують memory footprint.
- Алгоритм обробки важливіший за мікрооптимізації.
180. Що таке контекстні менеджери?
Контекстний менеджер керує ресурсом за протоколом __enter__/__exit__:
відкриття/ініціалізація та гарантоване завершення/cleanup.
Коротко:
- Дає безпечний lifecycle ресурсу.
- Працює через
with. - Зменшує кількість витоків ресурсів.
181. Як працює конструкція `with`?
with викликає __enter__ при вході в блок і __exit__ при виході, навіть
коли сталася помилка.
with open("data.txt", "r", encoding="utf-8") as f:
data = f.read()Коротко:
withгарантує cleanup.- Робить роботу з ресурсами декларативною.
- Рекомендований для файлів, локів, транзакцій, сесій.
182. Як створити власний контекстний менеджер?
Два підходи:
- клас з
__enter__/__exit__; - функція з
contextlib.contextmanager.
from contextlib import contextmanager
@contextmanager
def temp_flag():
yieldКоротко:
- Клас підходить для складного стану.
contextmanagerзручний для коротких сценаріїв.- Обидва дають гарантований cleanup.
183. Як Python керує пам'яттю?
CPython використовує reference counting і циклічний garbage collector.
Додатково є внутрішній алокатор (pymalloc) для дрібних об'єктів.
Коротко:
- Базовий механізм: лічильник посилань.
- GC прибирає циклічні посилання.
- Пам'ять звільняється не завжди миттєво на рівні ОС.
184. Що таке reference counting у Python і чому воно важливе для керування пам'яттю?
Кожен об'єкт має лічильник посилань. Коли він стає нулем, об'єкт можна звільнити. Це забезпечує швидке звільнення більшості короткоживучих об'єктів.
Коротко:
- Reference counting дає передбачуваний lifecycle об'єктів.
- Не вирішує циклічні посилання самостійно.
- Разом з GC формує повну модель керування пам'яттю.
185. Що таке garbage collection у Python?
Garbage collection у CPython знаходить і прибирає недосяжні цикли об'єктів, які не можуть бути очищені лише reference counting.
Коротко:
- GC доповнює reference counting.
- Особливо важливий для циклічних графів посилань.
- Його можна контролювати через модуль
gc.
186. Як отримати адресу пам'яті об'єкта у Python?
У CPython id(obj) часто відповідає адресі об'єкта в пам'яті (як ціле число).
Це діагностичний інструмент, а не стабільний зовнішній ідентифікатор.
Коротко:
id()дає identity об'єкта.- У CPython це зазвичай memory address.
- Не використовуйте для бізнес-логіки.
187. Для чого функція `getrefcount()` з модуля `sys`, як вона працює у Python?
sys.getrefcount(obj) повертає поточний лічильник посилань на об'єкт
(у CPython). Значення зазвичай на 1 більше через тимчасове посилання аргументу.
Коротко:
- Це інструмент діагностики memory-behavior.
- Корисний для аналізу витоків посилань.
- Не слід покладатися на нього в прикладній логіці.
188. Поясніть на прикладах різницю між mutable та immutable-об'єктами щодо reference counting і керування пам'яттю.
Immutable-об'єкти не змінюються, тому "зміна" створює новий об'єкт. Mutable-об'єкти змінюються in-place, і всі посилання бачать зміну.
a = "x"; b = a; a += "y" # новий об'єкт
x = [1]; y = x; y.append(2) # зміна того ж об'єктаКоротко:
- Immutable зменшують побічні ефекти.
- Mutable ефективніші для in-place модифікацій.
- Різниця критична для копіювання і спільних reference.
189. Яка різниця між shallow copy та deep copy у Python, коли використовувати кожен підхід?
Shallow copy копіює лише зовнішній контейнер, вкладені об'єкти залишаються спільними. Deep copy рекурсивно копіює всю структуру.
import copy
copy.copy(obj)
copy.deepcopy(obj)Коротко:
- Shallow достатній для "плоских" структур.
- Deep потрібен для ізольованої роботи з вкладеними mutable-даними.
- Deep copy дорожчий по часу і пам'яті.
190. Що таке modules і packages у Python?
Module це окремий .py-файл з кодом.
Package це каталог модулів (простір імен), що організує код у більші блоки.
Коротко:
- Module = одиниця коду.
- Package = структура для масштабування модулів.
- Розбиття на модулі/пакети покращує підтримуваність.
191. Як імпортувати module у Python?
Основні форми:
import module;import module as m;from module import name;from package import submodule.
Коротко:
- Імпорт завантажує модуль і робить його доступним у namespace.
- Alias (
as) покращує читабельність і уникнення конфліктів. - Віддавайте перевагу явним імпортам.
192. Які бувають типи imports?
Поширені типи:
- абсолютні;
- відносні;
- вибіркові (
from x import y); - alias-імпорти (
as); - динамічні (
importlib).
Коротко:
- Тип імпорту впливає на читабельність і підтримку.
- У продакшені переважно використовують абсолютні імпорти.
- Динамічні імпорти застосовують точково.
193. Як працює система імпортів?
Інтерпретатор шукає модуль по sys.path, завантажує його один раз і кешує в
sys.modules. Повторний імпорт використовує кеш.
Коротко:
- Імпорт = пошук + виконання модуля + кешування.
- Це пояснює, чому модульний код виконується при першому імпорті.
- Циклічні імпорти виникають через порядок ініціалізації модулів.
194. У чому різниця між абсолютним і відносним імпортом?
Абсолютний імпорт починається від кореня пакета (from app.utils import x).
Відносний імпорт використовує крапки (from .utils import x).
Коротко:
- Абсолютні імпорти читабельніші і стабільніші.
- Відносні зручні всередині пакета, але гірші для рефакторингу.
- Для великих проєктів краще стандартно тримати абсолютні.
195. Що робить функція `dir()`, як її можна застосовувати до modules?
dir(obj) повертає список доступних атрибутів/імен об'єкта.
Для модулів це швидкий спосіб подивитися API.
import math
dir(math)Коротко:
dir()допомагає в інтерактивному дослідженні модулів.- Корисний у REPL і дебазі.
- Не замінює офіційну документацію.
196. Поясніть роль конструкції `if __name__ == "__main__":` у Python-скриптах.
Цей блок виконується лише коли файл запущений як скрипт, а не імпортований як модуль. Це розділяє reusable-код і CLI-entrypoint.
Коротко:
- Дозволяє модуль і запускати, і імпортувати.
- Запобігає небажаному виконанню при імпорті.
- Стандартний патерн для скриптів.
197. Що означає файл `__init__.py` у Python package?
__init__.py позначає каталог як пакет і може ініціалізувати package-level API
(наприклад, реекспортувати класи/функції).
Коротко:
- Формує публічний інтерфейс пакета.
- Може містити мінімальну ініціалізацію.
- Не варто перевантажувати його важкою логікою.
198. Які best practices для стилю імпортів у Python-коді?
- групувати імпорти: stdlib, third-party, local;
- уникати
from x import *; - тримати імпорти на початку файлу (крім обґрунтованих lazy-import кейсів);
- використовувати сортування/форматування (
ruff,isort).
Коротко:
- Консистентні імпорти покращують читабельність.
- Явні імпорти зменшують конфлікти імен.
- Автоматизація інструментами прибирає ручні помилки.
199. Які популярні built-in modules існують у Python?
Приклади популярних модулів стандартної бібліотеки:
os,sys,pathlib,json,datetime,re,collections,itertools,functools,typing,asyncio,subprocess,logging,argparse.
Коротко:
- Стандартна бібліотека покриває більшість базових задач.
- Це знижує кількість зовнішніх залежностей.
- Варто добре знати stdlib перед додаванням сторонніх пакетів.
200. Що ви знаєте про пакет `collections`, які ще built-in modules використовувались?
collections надає високорівневі структури:
Counter,defaultdict,deque,namedtuple,ChainMap.
Типово комбінується з:
itertoolsдля ітераційних пайплайнів;functoolsдля кешування/функц. інструментів;pathlib,json,datetimeв прикладних задачах.
Коротко:
collectionsрозширює базові контейнери практичними структурами.- Часто підвищує простоту й продуктивність коду.
- Добре працює в парі з
itertools/functools.
201. Що повертає `sys.argv`?
sys.argv повертає список аргументів командного рядка:
sys.argv[0]— ім'я скрипта;- решта елементів — передані аргументи.
Коротко:
sys.argvце базовий інтерфейс CLI-аргументів.- Значення приходять рядками.
- Для складних CLI краще
argparse.
202. Який основний модуль для роботи з операційною системою у Python?
Основний модуль це os (разом з os.path), а для сучасного API шляхів
рекомендовано pathlib.
Коротко:
osдає доступ до process/env/filesystem API ОС.pathlibзручніший для роботи з шляхами.- У реальних проєктах часто використовують обидва.
203. Як перемішати елементи списку за допомогою модуля `random`?
Використовуйте random.shuffle(list_) для in-place перемішування.
import random
items = [1, 2, 3, 4]
random.shuffle(items)Коротко:
shuffleзмінює оригінальний список.- Працює лише зі списками (mutable sequence).
- Для нової копії:
random.sample(items, k=len(items)).
204. Що таке virtual environment?
Virtual environment це ізольоване середовище Python із власними пакетами й версіями залежностей для конкретного проєкту.
Коротко:
- Ізоляція прибирає конфлікти залежностей між проєктами.
- Стандартний інструмент:
venv. - Це базова практика для reproducible development.
205. Як працює `pip`?
pip встановлює, оновлює та видаляє пакети з індексів (здебільшого PyPI),
розв'язує залежності і ставить їх у поточне середовище.
Коротко:
pipце стандартний пакетний менеджер Python.- Працює в межах активного venv/інтерпретатора.
- Для стабільності важливі pinned-версії.
206. Що таке `requirements.txt`?
requirements.txt це файл зі списком залежностей (часто з точними версіями),
який використовується для відтворюваної інсталяції.
Коротко:
- Формат простий і сумісний з
pip install -r. - Найкраще фіксувати точні версії для production.
- Часто генерується з lock-файлів.
207. Що таке `pyproject.toml` і чому він став стандартом?
pyproject.toml це стандартизований файл конфігурації проєкту: metadata
пакета, build-system, налаштування інструментів (ruff, pytest, mypy тощо).
Коротко:
- Централізує конфігурацію Python-проєкту.
- Підтримується сучасним packaging tooling.
- Зменшує кількість розрізнених config-файлів.
208. Як керувати залежностями в сучасному Python-проєкті?
- ізолювати середовище (
venv); - визначати залежності в
pyproject.toml; - фіксувати lock/constraints для reproducibility;
- регулярно оновлювати з CI-перевірками.
Коротко:
- Керування залежностями має бути відтворюваним.
- Не змішуйте глобальні і проєктні пакети.
- Оновлення робіть контрольовано через тести.
209. Як правильно організувати структуру великого Python-проєкту?
- розділити код на доменні пакети;
- мати окремі шари:
api,services,domain,infrastructure; - виділити
tests/,scripts/,configs/; - тримати явні межі модулів та публічний API.
Коротко:
- Структура має відображати домен, а не випадкові технічні деталі.
- Чіткі межі зменшують циклічні залежності.
- Тести і tooling повинні бути first-class частиною дерева.
210. У чому різниця між автоматизованим і ручним тестуванням, які переваги автоматизованого тестування?
Ручне тестування виконується людиною крок за кроком. Автоматизоване тестування виконується скриптами/фреймворками.
Коротко:
- Автотести швидкі, повторювані і придатні для CI.
- Ручне тестування корисне для exploratory UX-сценаріїв.
- У продакшені потрібна комбінація обох підходів.
211. Що таке TDD (Test-Driven Development)?
TDD це цикл: написати failing-тест -> мінімальна реалізація -> рефакторинг.
Коротко:
- TDD формує API через тести.
- Дає швидкий feedback про регресії.
- Працює найкраще для модульної бізнес-логіки.
212. Які популярні фреймворки для тестування у Python?
Найпопулярніші: pytest, unittest (stdlib), також hypothesis для
property-based тестів.
Коротко:
pytestнайчастіше вибирають для нових проєктів.unittestкорисний як стандартний базовий інструмент.hypothesisпідсилює покриття edge-cases.
213. Що таке `unittest` у Python?
unittest це вбудований xUnit-фреймворк для тестів: test case класи,
assert-методи, setup/teardown, test runner.
Коротко:
- Частина стандартної бібліотеки.
- Підходить для консервативних середовищ без зовнішніх залежностей.
- Має більш "церемоніальний" синтаксис за pytest.
214. Що таке `pytest` і чим він відрізняється від `unittest` за синтаксисом і функціональністю?
pytest це фреймворк з простим синтаксисом функційних тестів, потужними
fixtures, параметризацією і екосистемою плагінів.
Коротко:
pytestменш шаблонний і зручніший для великих тест-сетів.unittestclass-based і стандартний у stdlib.- У сучасних проєктах переважає
pytest.
215. Опишіть, як метод `pytest.raises` використовується для тестування виникнення конкретних exception-ів в Python-коді.
pytest.raises(ExpectedError) перевіряє, що блок коду піднімає саме очікуваний
виняток.
import pytest
with pytest.raises(ValueError):
int("abc")Коротко:
- Тестує негативні сценарії явно.
- Захищає від "мовчазного" проходження помилок.
- Може перевіряти і повідомлення/атрибути винятку.
216. Що таке параметризація у тестах, як pytest підтримує її через `@pytest.mark.parametrize`?
Параметризація запускає один тест на кількох наборах вхідних даних.
У pytest це робиться декоратором @pytest.mark.parametrize.
Коротко:
- Менше дублювання тест-коду.
- Легко масштабувати кейси.
- Краща прозорість покриття вхідних сценаріїв.
217. Як можна у pytest задати власні назви тестів при параметризованому тестуванні для кращої читабельності?
Використовуйте параметр ids у parametrize.
@pytest.mark.parametrize("value,expected", [(2, True), (3, False)], ids=["even", "odd"])Коротко:
idsробить output тест-рану зрозумілішим.- Полегшує діагностику падінь.
- Особливо корисно при великій кількості кейсів.
218. Що таке Arrange/Setup у тестуванні, чому це важливо?
Arrange/Setup це підготовка тестового стану: дані, об'єкти, моки, середовище. Якісний setup робить тест детермінованим.
Коротко:
- Нестабільний setup = flaky-тести.
- Добрий setup ізолює тест від зовнішніх ефектів.
- Чіткий Arrange покращує читабельність сценарію.
219. Які етапи містить Cleanup/Teardown і чому це важливо?
Teardown прибирає все, що створив тест: тимчасові файли, з'єднання, моки, тестові записи в БД.
Коротко:
- Cleanup забезпечує ізоляцію між тестами.
- Зменшує побічні ефекти і флейковість.
- У pytest це зручно робити через fixture finalizers/yield-fixtures.
220. У чому різниця між методами `setUp()` та `setUpClass()` у unittest?
setUp() виконується перед кожним тест-методом.
setUpClass() (classmethod) виконується один раз перед усіма тестами класу.
Коротко:
setUpдля пер-тестової ізоляції.setUpClassдля дорогих спільних ресурсів.- Надмірне спільне state через
setUpClassможе ускладнювати тести.
221. Що таке mock object і як він допомагає підвищити якість тестів?
Mock object це підставний об'єкт, який імітує залежність і дозволяє контролювати поведінку/перевіряти виклики.
Коротко:
- Моки ізолюють юніт від зовнішніх сервісів.
- Роблять тести швидшими і стабільнішими.
- Дають перевірку взаємодій, не лише результату.
222. Яка різниця між використанням `mock.patch` у unittest та `monkeypatch` у pytest для мокання об'єктів?
mock.patch з unittest.mock патчить об'єкти за шляхом імпорту і має багату
API для перевірки викликів.
monkeypatch у pytest простіше змінює атрибути/env/dict на час тесту.
Коротко:
patchпотужніший для mock-assertion сценаріїв.monkeypatchзручний для швидких тестових підмін.- Часто їх комбінують залежно від кейсу.
223. Яке призначення параметра `scope` у pytest fixtures?
scope визначає життєвий цикл fixture: function, class, module,
package, session.
Коротко:
- Менший scope = краща ізоляція.
- Більший scope = швидший ран великих тест-сетів.
- Вибір scope це баланс швидкості та незалежності.
224. Що таке складність алгоритму і як вона визначається?
Складність алгоритму оцінює, як ростуть витрати часу/пам'яті зі збільшенням
розміру вхідних даних n.
Коротко:
- Вимірюють часову та просторову складність.
- Оцінка зазвичай асимптотична.
- Допомагає обирати алгоритм і структури даних.
225. Поясніть поняття big O-нотації і її значення в оцінці складності алгоритмів.
Big-O описує верхню межу зростання вартості алгоритму при великому n,
ігноруючи константи і нижчі члени.
Коротко:
- Big-O показує масштабованість, а не точний час.
- Дає мову для порівняння алгоритмів.
- Критично важлива для продуктивності на великих обсягах.
226. Які види алгоритмічної складності найчастіше зустрічаються?
Найчастіші: 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)) алгоритмами.
Приклади:
- пошук максимуму/мінімуму;
- фільтрація елементів;
- підрахунок частот через
Counter; - перевірка умов для кожного елемента.
Коротко:
- O(n) означає один прохід по даних.
- Це оптимально для багатьох агрегацій.
- Лінійні алгоритми добре масштабуються.
228. Як працює `lru_cache` і коли його використовувати?
functools.lru_cache кешує результати функції за аргументами і повертає
готове значення при повторних викликах.
Коротко:
- Ефективний для pure-функцій із повторюваними вхідними даними.
- Не підходить для функцій із side effects.
maxsizeконтролює обсяг кешу в пам'яті.
229. Як профілювати продуктивність Python-коду?
Базові інструменти:
timeitдля мікровимірювань;cProfile/pstatsдля call-level профілю;py-spy/scaleneдля production-like аналізу.
Коротко:
- Оптимізуйте лише після вимірювань.
- Профілюйте реалістичні сценарії навантаження.
- Фіксуйте baseline до/після змін.
230. Які основні способи оптимізації Python-коду?
- обрати правильні структури даних;
- зменшити асимптотичну складність;
- уникати зайвих копій;
- використовувати генератори/lazy pipeline;
- кешувати дорогі обчислення;
- виносити hot paths у C/Rust/NumPy, якщо потрібно.
Коротко:
- Найбільший приріст дає алгоритм, не синтаксичний твік.
- Оптимізація має спиратися на профілювання.
- Читабельність не варто жертвувати без виміряної вигоди.
231. Коли варто використовувати C extensions або PyPy?
C extensions доречні для вузьких CPU-hotspots і інтеграції з нативними бібліотеками. PyPy доречний, коли довгоживучий pure-Python код виграє від JIT.
Коротко:
- C extension: максимальна продуктивність ціною складності збірки.
- PyPy: потенційний приріст без переписування на C.
- Вибір робиться на основі бенчмарків вашого workload.
232. Що таке memory profiling?
Memory profiling це вимірювання, де і скільки пам'яті споживає код у часі, щоб знайти витоки і важкі ділянки.
Коротко:
- Показує memory hot spots і піки.
- Корисний для batch/data workloads.
- Інструменти:
tracemalloc,memory_profiler,scalene.
233. Що таке GIL (Global Interpreter Lock)?
GIL це механізм у CPython, який дозволяє лише одному thread одночасно виконувати Python bytecode в межах процесу.
Коротко:
- GIL впливає на CPU-bound multithreading.
- Для I/O-bound задач threads все ще корисні.
- Для CPU-паралелізму частіше використовують multiprocessing.
234. Як глобальний інтерпретаторний лок Python (GIL) впливає на concurrency у CPython та які наслідки це має для multithreading?
Через GIL потоки в CPython не виконують Python bytecode truly-parallel для CPU-bound коду. Вони кооперативно перемикаються.
Наслідки:
- для I/O-bound потоків ефект добрий (очікування I/O перекривається);
- для CPU-bound задач приріст від threads зазвичай обмежений.
Коротко:
- GIL обмежує паралелізм потоків у CPU-bound сценаріях.
- Threads залишаються корисними для мережі/диска.
- Для CPU використовуйте процеси або нативні обчислення.
235. Як GIL впливає на продуктивність?
GIL майже не заважає в I/O-bound задачах, але стримує throughput CPU-bound multithreaded Python-коду в одному процесі.
Коротко:
- Вплив GIL залежить від типу навантаження.
- CPU-bound + threads у CPython часто не масштабується.
- Архітектуру слід обирати за профілем задач.
236. Поясніть концепцію threading у Python та як він відрізняється від multiprocessing.
threading запускає кілька потоків в одному процесі зі спільною пам'яттю.
multiprocessing запускає окремі процеси з окремою пам'яттю.
Коротко:
- Threads легші і зручні для I/O-bound.
- Processes дають реальний CPU-паралелізм.
- Процеси мають вищі накладні витрати IPC/створення.
237. Коли використовувати multiprocessing замість threading?
Коли задача CPU-bound і потребує використання кількох ядер у CPython. Приклади: heavy parsing, image/video processing, чисельні обчислення.
Коротко:
- CPU-bound -> зазвичай
multiprocessing. - I/O-bound -> зазвичай
threading/asyncio. - Враховуйте вартість серіалізації між процесами.
238. Яка різниця між concurrency та parallelism у програмуванні, коли і яке з них доцільно використовувати?
Concurrency це перекривання задач у часі. Parallelism це одночасне фізичне виконання задач на кількох ядрах.
Коротко:
- Concurrency корисний для I/O затримок.
- Parallelism потрібен для CPU-intensive обчислень.
- У Python вибір інструмента залежить від типу bottleneck.
239. Що таке concurrency у Python?
Concurrency у Python це організація виконання кількох задач так, щоб вони прогресували разом: через threads, asyncio або процеси.
Коротко:
- Це про керування багатьма задачами, не обов'язково паралельно.
- Дозволяє підвищити throughput I/O сценаріїв.
- Потребує акуратного дизайну синхронізації та скасування.
240. У чому різниця між IO-bound і CPU-bound задачами?
IO-bound задачі переважно чекають мережу/диск/БД. CPU-bound задачі переважно витрачають час на обчислення процесора.
Коротко:
- IO-bound добре масштабується через asyncio/threads.
- CPU-bound краще масштабується через multiprocessing/нативний код.
- Спочатку визначте bottleneck через профілювання.
241. Для чого потрібен модуль `asyncio` у Python і як він дозволяє реалізувати асинхронне програмування?
asyncio надає event loop, task scheduling і async primitives для кооперативної
конкурентності в I/O-bound задачах.
Коротко:
- Дозволяє ефективно обслуговувати багато I/O-операцій.
- Базується на
async/await. - Добре підходить для мережевих сервісів і клієнтів.
242. Чим відрізняється синхронне і асинхронне програмування у Python?
Синхронне: виклик блокує поточний потік до завершення.
Асинхронне: await віддає керування event loop, поки операція очікує I/O.
Коротко:
- Async зменшує idle-time під час I/O.
- Sync простіший для лінійної логіки.
- Async додає складність управління задачами і cancellation.
243. Що таке `async`/`await`?
async def визначає coroutine-функцію.
await призупиняє coroutine до готовності awaitable і повертає контроль loop.
Коротко:
- Це синтаксис асинхронної кооперативної моделі.
- Використовується разом з
asyncioта async-бібліотеками. awaitможливий лише всерединіasync def.
244. Як працює `asyncio`?
asyncio запускає event loop, який виконує tasks (coroutines), перемикаючись у
точках await і плануючи готові операції I/O.
Коротко:
- Один потік може обслуговувати багато I/O задач.
- Планування кооперативне, не preemptive.
- Блокувальні виклики потрібно виносити з loop.
245. Що таке event loop?
Event loop це планувальник, який відстежує події/готовність I/O і запускає відповідні callbacks/coroutines.
Коротко:
- Центральний компонент asyncio-моделі.
- Керує життєвим циклом async задач.
- Визначає, коли яка coroutine продовжує виконання.
246. Як `asyncio` дозволяє реалізувати асинхронне програмування і які основні компоненти задіяні в asyncio-коді?
Ключові компоненти:
- event loop;
- coroutine (
async def); - task (
asyncio.create_task); - awaitables (futures, tasks, coroutines);
- синхронізаційні примітиви (
Lock,Queue,Semaphore).
Коротко:
asyncioпоєднує планування і async-API в єдину модель.- Задачі кооперативно ділять один потік виконання.
- Архітектура має враховувати таймаути, retry і cancellation.
247. Коли `asyncio` не дає переваг?
Коли workload CPU-bound або основні бібліотеки блокувальні й не мають async API. Також не окупається для простих коротких скриптів.
Коротко:
- Async не прискорює чисті обчислення.
- Без неблокувальних I/O бібліотек вигода мінімальна.
- Складність async має бути виправдана навантаженням.
248. Як працює asyncio cancellation?
Скасування задачі (task.cancel()) піднімає CancelledError у coroutine.
Код має коректно обробляти cleanup у try/finally.
Коротко:
- Cancellation це звичайний control-flow у async.
- Потрібно закладати обробку скасування в дизайн coroutine.
- Ігнорування cancellation призводить до "завислих" задач.
249. Що таке `contextvars`?
contextvars дає context-local змінні, безпечні для async/threads сценаріїв.
Корисно для request-id, correlation-id, tenant-context.
Коротко:
- Альтернатива глобальному state у конкурентному коді.
- Значення ізольоване per-context.
- Покращує трасування і observability.
250. Які best practices варто застосовувати при написанні Python-коду?
- дотримуватись PEP 8 та автоматизувати форматування;
- писати явні type hints для публічного API;
- використовувати
withдля ресурсів; - покривати бізнес-логіку тестами;
- уникати передчасної оптимізації, профілювати;
- тримати модулі невеликими з чіткою відповідальністю;
- керувати залежностями через
pyproject.toml+ lock strategy.
Коротко:
- Читабельність і передбачуваність важливіші за "трюки".
- Якість тримається на автоматизації: lint, types, tests, CI.
- Архітектурна простота зменшує вартість підтримки.