Ключевое слово def в Python превращает хаос кода в элегантные блоки, которые можно вызвать сколь угодно, словно вызывая верного слугу. Это не просто синтаксис – это фундамент программирования, где простая функция может рассчитывать факториал или управлять асинхронными запросами к серверу. Представьте: вместо копировать строки print по сто раз, вы пишете def greet(name): print(f”Привет, {name}!”) – и готово, код чист, как свежий снег.
Функция, созданная через def, принимает параметры, выполняет логику и возвращает результат через return или молча завершается с None. Вот базовый пример: def suma(a, b): return a+b. Вызоваете suma(5, 3) – получаете 8. Просто, мощное и незаменимое для любого скрипта, от автоматизации офисных заданий до машинного обучения.
Но def прячет гораздо больше: от переменного количества аргументов до декораторов, добавляющих логирование одной строчкой. Эта статья разберет все по косточкам, с примерами, которые копируйте и тестируйте прямо в интерпретаторе.
Что скрывается за def: суть функций в Python
Def означает “define” – определить. Оно запускает создание новой функции, которая становится объектом первого класса: ее можно передавать в аргументах, возвращать из других функций или хранить в словаре. Функции делают код модульным, повторно используемым и лёгким для тестирования. Без них Python превратился бы в спагетти с копипастом.
Каждая функция имеет свое пространство имен – локальную область видимости. Переменные внутри def живут только там, пока функция выполняется. Это защищает от конфликтов, будто каждая комната в доме имеет свои шкафы. А правило LEGB (Local, Enclosing, Global, Built-in) определяет где искать переменную: сначала локально, затем во внешних функциях, глобально или встроенно.
Согласно официальной документации Python (docs.python.org), def создает символическую таблицу для локальных переменных при каждом вызове. Это гарантирует изоляцию, но требует внимания к global или nonlocal для изменения внешних данных.
Синтаксис def: как правильно создавать функции
Базовый шаблон прост: def название(параметры): с отступлением уходит тело. Название – snake_case, потому что Python любит нижний регистр с подчеркиваниями. Параметры в скобках опциональны, двоеточие обязательно, тело – с отступлением в 4 пробела.
Добавьте докстринг – строчку в тройных кавычках в начале тела. Он становится __doc__ атрибутом функции, полезным для help(функция) или автодокументации. Вот пример:
def calculate_area(radius: float) -> float:
"""
Обчислює площу кола за формулою πr².
Args:
radius: Радіус кола (позитивне число).
Returns:
Площа кола.
"""
import math
return math.pi * radius ** 2
Теперь help(calculate_area) выдаст хорошее описание. Докстринги – не прихоть, а стандарт PEP 257 для профессионального кода.
- Напишите def с названием, описывающим действие: не func1, а process_user_data.
- Добавьте параметры после двоеточия.
- Тело с отступлением: логика, return если нужно.
- Тестируйте вызовом: result = calculate_area(5); print(result).
Этот ритуал делает код самодокументированным. А теперь представьте масштабы: тысячи функций в проекте, и все ясно, потому что синтаксис на высоте.
Параметры функций: от базовых до *args и **kwargs
Параметры – это дверь данных в функцию. Позиционные передаются по порядку, ключевые – как ключ = значение. По умолчанию параметры позиционные, но с 3.8 добавлено / для только-позиционных и * для только-ключевых.
Значения по умолчанию экономят усилия: def greet(name=”Гость”): print(f”Привет, {name}!”). Вызовите greet() – "Привет, Гость!". Но осторожно с мутабельными: def bad_func(lst=[]): lst.append(1); return lst. Каждый вызов добавляет в один список! Фикс: lst=None, если не lst: lst=[].
Сменное количество: *args собирает позиционные в tuple, **kwargs – ключевые в dict. Идеально для гибких API.
def universal_sum(*args, separator=', '):
return separator.join(map(str, args))
print(universal_sum(1, 2, 3)) # 1, 2, 3
def info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
info(name="Олена", age=28)
Комбинируйте: def full_example(a, b=0, *args, mode='auto', **kwargs): …. Здесь a/b позиционные, args – лишние позиционные, mode/kwargs – ключевые.
| Тип параметра | Синтаксис | Пример вызова | Видимые результаты |
|---|---|---|---|
| Позиционный | a | func(10) | а = 10 |
| По умолчанию | б = 5 | func(10) или func(10,20) | b=5 или b=20 |
| * аргументы | *numbers | func(1,2,3) | numbers=(1,2,3) |
| ** kwargs | **данные | func(x=1,y=2) | data={'x':1,'y':2} |
| Только позиционный (/) | а / | func(10) | Ошибка при func(a=10) |
| Только ключевой (*) | * , k=1 | func(k=10) | Ошибка при func(10) |
Источники данных документация Python (docs.python.org). Такая таблица помогает быстро ориентироваться, потому что параметры – ключ к гибкости. В реальных проектах args/kwargs делают функции универсальными, как швейцарский нож.
Return: как функции возвращают сокровища
Без return функция возвращает None, молча завершая работу. Но с return value – это ваш трофей. Может возвращать числа, списки, dict или даже функции! def power(base, exp): return base ** exp. Готово, reusable математика.
Ранние return экономят код: def is_even(n): if n %2: return False; return True. Кратко и ясно. А множественные return? Python позволяет, потому что функция завершается на первом.
Важно: return из tuple распаковывается автоматически в присвоении: a, b = divmod(10,3). Это магия для множественных значений.
Область видимости: local, global, nonlocal в действии
Локальные переменные рождаются в def и гибнут после. Внешние видятся для чтения, но изменение создает локальную копию. Хотите менять глобальную? global x. Для вложенных – nonlocal x.
- Local: x = 5 внутри def – отдельная.
- Enclosing: у outer def y=10; inner видит y.
- Global: global z меняет модульную.
- Built-in: print, len – всегда доступны.
Пример замыкания (closure): def multiplier(factor): def inner(n): return n* factor; return inner. mult2 = multiplier(2); mult2(5) == 10. Factor "запоминается"!
Эти нюансы спасают от багов, делая код предполагаемым.
Вложенные функции и замыкания: помнящие функции
Def внутри def – вложенная функция. Она видит внешние переменные, создавая closure. Полезно для фабрик: def create_adder(x): def adder(y): return x + y; return adder.
Из nonlocal меняйте внешние: def counter(): count = 0; def increment(): nonlocal count; count +=1; return count; return increment. Каждый вызов увеличивает!
Замыкание – основа декораторов и callback'ов. Вы не поверите, насколько это мощно для станных функций без классов.
Декораторы: @ над def для суперсил
Декоратор – функция, оборачивающая другую, добавляя фичи. Синтаксис: @timer def slow_func(): …. Где timer – def, возвращающий wrapper.
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"{func.__name__} took {time.time()-start:.2f}s")
return result
return wrapper
@timer
def factorial(n):
return 1 if n == 0 else n * factorial(n-1)
factorial(100)
Результат: время рекурсии! functools.wraps сохраняет метаданные. Декораторы – для логов, кэша (lru_cache), авторизации. С Python 3.9 @ из assignment_expression.
Практические кейсы: def в реальной жизни
Кейс 1: Веб-скрапинг. def fetch_data(url): import requests; return requests.get(url).text. С декоратором retry для ошибок сети.
Кейс 2: Data science. def preprocess(df, columns): for col in columns: df[col] = df[col].fillna(0); return df. Из *columns для гибкости.
Кейс 3: сервер API (Flask/FastAPI). @app.route('/') def home(): return “Hello!”. Асинхронно: async def api_call(): await asyncio.sleep(1); return data.
Кейс 4: Автоматизация. def backup_files(folder, days=7): shutil.copytree… Из **options для формата архива. Эти примеры экономят часы!
Генераторы: def из yield для ленивых последовательностей
Замените return на yield – получите генератор. Не перечень в памяти, а итератор on-demand. def fib_gen(n): a,b=0,1; while a
для числа в fib_gen(1000): print(num). Экономит память для гигантских данных. next(gen) – следующий элемент. Send(), throw() для интерактивности.
Генераторы – must-have для потоков данных, как на pandas либо ML датасетах.
Async def: асинхронность для скорости
Async def создает коротин. Используйте await для IO: async def fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as resp: return await resp.text(). asyncio.run(fetch('site.com')).
Идеально для серверов, API, где ожидание – враг. С Python 3.5+ стандарт, в 3.13 лучшие typing для async.
Аннотации типов: делаем def типобезопасным
def add(a: int, b: int) -> int: return a+b. Mypy проверит! typing.List[str], Union[int,float]. Из 3.12 type params [T], 3.13 defaults.
Не влияет на runtime, но IDE подскажет ошибки. Типы – мостик к enterprise-коду.
Lambda: короткие def без имени
Lambda args: expression. sorted(items, key=lambda x:x.age). Быстро для map/filter. Ограничено одним выражением, но молниеносно.
Комбинируйте с functools.partial для частичного применения.
Типичные ошибки и как их избегать
1. Mutable defaults – фикс из None. 2. Забыли return – None surprise. 3. Scope баги без nonlocal. 4. Рекурсия без базы – стек overflow. Тест с pytest!
Функции по def – ваш арсенал. Экспериментируйте, стройте проекты, и Python раскроется полностью.