Функції та функціональне програмування¶
Довідник по функціях у Python — від базового визначення до замикань, декораторів і функціонального стилю.
1. Базові концепції функцій¶
Функція — це іменований блок коду, призначений для вирішення однієї конкретної задачі. Використання функцій дозволяє уникнути дублювання коду (принцип DRY — Don't Repeat Yourself), робить програму більш читабельною, полегшує її тестування та супровід.
Створення та виклик¶
У Python функції визначаються за допомогою ключового слова def, за яким слідує ім'я функції, круглі дужки з параметрами та двокрапка. Тіло функції виділяється відступами. Функція не виконується в момент її визначення; код усередині неї запускається лише тоді, коли функція викликається.
Повернення значень (return)¶
Функції можуть обчислювати результат і повертати його в основну програму за допомогою інструкції return. Якщо функція не містить return, вона за замовчуванням повертає спеціальне значення None. Також Python дозволяє повертати одразу декілька значень (вони автоматично пакуються в кортеж).
Документування (Docstrings)¶
Гарним тоном є використання рядків документації (docstrings) — багаторядкових коментарів у потрійних лапках """...""", які розміщуються одразу після визначення def. Цей текст автоматично зберігається у спеціальному атрибуті функції __doc__. Прочитати його можна за допомогою вбудованої функції help(ім'я_функції).
def greet_user(name):
"""
Виводить просте вітання для вказаного користувача.
"""
return f"Hello, {name}!"
# Виклик функції
message = greet_user("Ivan")
print(message)
# Перегляд документації
help(greet_user)
Результат:
Hello, Ivan!
Help on function greet_user in module __main__:
greet_user(name)
Виводить просте вітання для вказаного користувача.
2. Робота з аргументами та параметрами¶
Python має надзвичайно гнучкий механізм передачі аргументів.
- Позиційні та іменовані аргументи: аргументи можна передавати в тому порядку, в якому вони визначені (позиційні), або вказуючи їхні імена (іменовані), що робить порядок неважливим.
-
Значення за замовчуванням: параметрам можна задати значення за замовчуванням (наприклад,
def func(a, b=2):). Тоді при виклику цей аргумент можна пропустити.Мутабельні значення за замовчуванням
Ніколи не використовуйте змінні об'єкти (як-от порожні списки
[]) як значення за замовчуванням, оскільки вони зберігатимуть свій стан між викликами. Замість цього використовуйтеNone. -
Довільна кількість аргументів (
*argsта**kwargs):- символ
*перед параметром (зазвичай*args) збирає всі додаткові позиційні аргументи в кортеж; - символи
**(зазвичай**kwargs) збирають додаткові іменовані аргументи у словник.
- символ
def my_func(a, b=10, *args, **kwargs):
print("a:", a)
print("b:", b)
print("args:", args)
print("kwargs:", kwargs)
my_func(1, 20, 30, 40, x=100, y=200)
Результат:
Повний порядок параметрів у def¶
| Вид | Позначка | Передача | Тип усередині |
|---|---|---|---|
| Positional-only (3.8+, PEP 570) | ліворуч від / |
лише за позицією | значення |
| Positional-or-keyword | після /, до *args / * |
за позицією або за іменем | значення |
| Var-positional | *args |
збирає зайві позиційні | tuple |
| Keyword-only (PEP 3102) | після *args або поодинокої * |
лише key=value |
значення |
| Var-keyword | **kwargs, завжди останній |
збирає зайві іменовані | dict |
Значення за замовчуванням — не окремий вид: його можуть мати і positional-or-keyword (default_pos=10), і keyword-only (kw_default=20) параметри. Серед параметрів, які передаються за позицією, параметри зі значенням за замовчуванням стоять після обов'язкових. Для keyword-only такого обмеження немає.
Порушення порядку — SyntaxError ще до запуску:
def f(a=1, b): ... SyntaxError: parameter without a default follows parameter with a default
def f(**kw, *args): ... SyntaxError: arguments cannot follow var-keyword argument
Правила виклику¶
- Спершу позиційні, потім іменовані:
f(a=1, 2)→SyntaxError: positional argument follows keyword argument. - Одне значення — один раз:
f(1, a=2)→TypeError: f() got multiple values for argument 'a'. - Іменовані аргументи можна передавати в будь-якому порядку.
Розпаковка * і **¶
| Де | Запис | Результат |
|---|---|---|
| виклик | f(*[1, 2, 3]) |
f(1, 2, 3); працює з list, tuple, set (порядок не гарантований), str, range, генератором; dict віддає ключі |
| виклик | f(**{"x": 1}) |
f(x=1); ключі мають збігатися з іменами параметрів |
| присвоєння | first, *rest = [1, 2, 3] |
first = 1, rest = [2, 3] (завжди list) |
| літерал | [*a, *b], (*a, *b), {*a, *b} |
нова колекція з елементів a і b |
| літерал | {**d1, **d2} |
злиття словників, при однакових ключах перемагає правий |
| прокидання | def w(*args, **kwargs): return f(*args, **kwargs) |
посередник з будь-якою сигнатурою (основа декораторів) |
Пастки¶
- Мутабельне значення за замовчуванням обчислюється один раз при виконанні
defі зберігається вfunc.__defaults__(для keyword-only — у__kwdefaults__). Виправлення:None+ новий об'єкт у тілі. - Call-by-sharing: параметр — ще одне ім'я для того самого об'єкта. Присвоєння
x = ...всередині ззовні не видно, а мутаціюx.append(...)видно. **kwargsковтає описки:debyg=Trueмовчки потрапляє в словник. Якщо імена відомі, оголошуй їх явно як keyword-only:def f(data, *, debug=False).
Детальні приклади з логуванням: урок 8 та конспект module_1/lessons/lesson_08_functions/notes_functions.ipynb, Частина 6.
3. Область видимості (Scopes)¶
Змінні, створені всередині функції, є локальними і не існують за її межами. Якщо функція намагається знайти змінну, Python шукає її за правилом LEGB:
- Local (Локальна) — всередині функції.
- Enclosing (Охоплююча) — у зовнішній функції (якщо функції вкладені).
- Global (Глобальна) — на рівні модуля/файлу.
- Built-in (Вбудована) — базові імена Python (наприклад,
len,print).
Щоб змінити глобальну змінну всередині функції, використовується ключове слово global. Для зміни змінної з охоплюючої (зовнішньої) функції використовується nonlocal.
👉 Це лише вступ, потрібний для розуміння наступних розділів. Повний розбір LEGB (з покроковими прикладами кожного рівня, пастками на кшталт UnboundLocalError і тим, що саме повертають locals()/globals()) — на окремій сторінці: Простори імен: locals(), globals(), LEGB.
4. Функціональне програмування (FP) в Python¶
Хоча Python не є суто функціональною мовою програмування, він містить потужний арсенал інструментів для написання коду у функціональному стилі. Основа ФП — обчислення без зміни глобального стану (використання "чистих функцій", які не мають побічних ефектів).
Функції як об'єкти першого класу¶
У Python абсолютно все є об'єктами, і функції — не виняток. Функцію можна присвоїти змінній, передати як аргумент іншій функції (функції вищого порядку) або повернути як результат.
Анонімні функції (lambda)¶
Для створення коротких, одноразових функцій використовується оператор lambda. Вони містять лише один вираз, результат якого автоматично повертається.
Основні інструменти: map, filter, reduce¶
map(func, iterable)— застосовує функцію до кожного елемента послідовності.filter(func, iterable)— повертає лише ті елементи, для яких функція повернулаTrue.reduce(func, iterable)— послідовно застосовує функцію до пар елементів, "згортаючи" послідовність до одного значення. Потребує імпорту зfunctools.
List comprehensions замість map/filter
У сучасному Python замість map та filter часто віддають перевагу генераторам списків (list comprehensions), оскільки вони читаються легше.
from functools import reduce
# Лямбда-функція
add_lambda = lambda x, y: x + y
print("Лямбда 5+3 =", add_lambda(5, 3))
nums = [1, 2, 3, 4, 5]
# Map: підносимо до квадрату
squares = list(map(lambda x: x**2, nums))
print("Map (квадрати):", squares)
# Filter: залишаємо лише парні
evens = list(filter(lambda x: x % 2 == 0, nums))
print("Filter (парні):", evens)
# Reduce: сума всіх чисел
total = reduce(lambda a, b: a + b, nums)
print("Reduce (сума):", total)
Результат:
Корисні модулі для ФП: operator та functools¶
- Модуль
operator— містить функції, що відповідають стандартним математичним операторам. Замістьlambda x, y: x + yможна імпортуватиoperator.add. functools.partial— дозволяє "зафіксувати" частину аргументів функції, створивши нову функцію з меншою кількістю параметрів (часткове застосування).
from functools import partial
import operator
def power(base, exp):
return base ** exp
# Створюємо нову функцію, де exp завжди дорівнює 2
square = partial(power, exp=2)
print("Partial (5^2):", square(5))
# Використання operator замість lambda в reduce
total_op = reduce(operator.add, nums)
print("Reduce з operator.add:", total_op)
Результат:
5. Просунуті техніки на базі функцій¶
- Вкладені функції та замикання (closures): Python дозволяє визначати функцію всередині іншої функції. Вкладена функція може запам'ятовувати та використовувати змінні із зовнішньої (охоплюючої) функції навіть після того, як зовнішня функція завершила свою роботу. Це явище і називається замиканням.
- Декоратори: спеціальні функції-обгортки, які приймають іншу функцію як аргумент, модифікують її поведінку і повертають нову функцію. Застосовуються за допомогою символу
@перед оголошеннямdef. Ідеально для логування або кешування. - Рекурсія: функція може викликати саму себе. Корисно для обробки деревоподібних структур даних або математичних алгоритмів (наприклад, факторіал), проте завжди потрібно передбачати умову виходу, щоб уникнути нескінченного циклу.
# --- Приклад 1: Декоратор ---
def uppercase_decorator(func):
def wrapper(*args, **kwargs):
original_result = func(*args, **kwargs)
return original_result.upper() # Змінюємо поведінку (робимо великими літерами)
return wrapper
@uppercase_decorator
def say_hi(name):
return f"Привіт, {name}"
print("Декоратор:", say_hi("Олексій"))
# --- Приклад 2: Рекурсія ---
def factorial(n):
if n == 1: # Умова виходу (Base case)
return 1
return n * factorial(n - 1) # Рекурсивний виклик
print("\nРекурсія (5!):", factorial(5))
Результат:
👉 Практичне застосування функцій на курсі — рефакторинг звіту кафе у Уроці 7; декоратори як окрема тема — Урок 9.