Функции в Python — полный гайд для аналитика

Коротко

Функция в Python — блок кода, который можно вызвать по имени сколько угодно раз. Объявляется через def, принимает аргументы, возвращает результат через return. Для аналитика функции — способ не дублировать код: вычисление метрики, очистка данных, пакетная обработка файлов. На собеседованиях спрашивают про типы аргументов, область видимости и подводные камни с мутабельными значениями по умолчанию.

Синтаксис def и return

def calc_conversion(visitors, buyers):
    """Считает конверсию."""
    if visitors == 0:
        return 0.0
    return buyers / visitors

rate = calc_conversion(1000, 32)
print(rate)  # 0.032

def создаёт функцию с именем. return возвращает значение — без него функция вернёт None. Строка в тройных кавычках сразу после def — docstring. Она не влияет на выполнение, но видна через help(calc_conversion).

Возврат нескольких значений

Python позволяет вернуть несколько значений через кортеж:

def describe_metric(values):
    total = sum(values)
    avg = total / len(values)
    return total, avg

total, avg = describe_metric([100, 200, 150])
print(total)  # 450
print(avg)    # 150.0

Технически return total, avg возвращает кортеж (450, 150.0). Распаковка через total, avg = ... — стандартный паттерн.

Аргументы по умолчанию

def format_metric(value, decimals=2, suffix='%'):
    return f"{value:.{decimals}f}{suffix}"

format_metric(0.156)           # '0.16%'
format_metric(0.156, 1)        # '0.2%'
format_metric(1500, 0, ' ₽')  # '1500 ₽'

Аргументы со значением по умолчанию идут после обязательных. При вызове их можно опустить — подставится дефолт.

Позиционные и именованные аргументы

Аргументы можно передавать по позиции или по имени (keyword):

def retention(cohort_size, retained, period='d7'):
    rate = retained / cohort_size
    return f"{period}: {rate:.1%}"

# Позиционные
retention(500, 120)             # 'd7: 24.0%'

# Именованные — порядок не важен
retention(retained=120, cohort_size=500, period='d30')

Именованные аргументы делают вызов понятнее — особенно когда параметров много.

*args и **kwargs

*args собирает лишние позиционные аргументы в кортеж, **kwargs — именованные в словарь.

def log_event(event_name, *properties, **options):
    print(f"Event: {event_name}")
    print(f"Properties: {properties}")
    print(f"Options: {options}")

log_event('purchase', 'premium', 'annual', user_id=42, source='bot')
# Event: purchase
# Properties: ('premium', 'annual')
# Options: {'user_id': 42, 'source': 'bot'}

На практике аналитик чаще встречает **kwargs при написании обёрток — например, функция, которая прокидывает параметры в pd.read_csv():

def load_csv(path, **kwargs):
    df = pd.read_csv(path, **kwargs)
    df.columns = df.columns.str.strip().str.lower()
    return df

df = load_csv('data.csv', sep=';', encoding='cp1251')

Область видимости: local vs global

Переменные внутри функции — локальные. Они не видны снаружи:

x = 10  # глобальная

def foo():
    x = 20  # локальная — другая переменная
    print(x)

foo()    # 20
print(x) # 10 — глобальная не изменилась

Чтобы изменить глобальную из функции, нужно global x — но это антипаттерн. Функция должна получать данные через аргументы и возвращать результат через return.

lambda vs def

lambda — анонимная функция из одного выражения. def — полноценная функция с именем, docstring и несколькими строками.

# lambda — для одноразовых колбэков
sorted(data, key=lambda x: x['date'])

# def — для всего остального
def clean_phone(phone):
    """Удаляет пробелы и дефисы из номера."""
    return phone.replace(' ', '').replace('-', '')

Правило: если функция нужна один раз как аргумент — lambda. Если нужно имя, повторное использование или больше одной строки — def.

Практические примеры для аналитика

Расчёт метрики с защитой от деления на ноль

def safe_ratio(numerator, denominator, default=0.0):
    if denominator == 0:
        return default
    return numerator / denominator

cr = safe_ratio(buyers, visitors)
arpu = safe_ratio(revenue, users)

Очистка данных

def clean_column(series):
    """Приводит строковый столбец к нижнему регистру и убирает пробелы."""
    return series.str.strip().str.lower()

df['city'] = clean_column(df['city'])
df['category'] = clean_column(df['category'])

Пакетная обработка файлов

import glob
import pandas as pd

def load_and_concat(pattern, **read_kwargs):
    """Загружает все CSV по шаблону и склеивает в один DataFrame."""
    files = glob.glob(pattern)
    frames = [pd.read_csv(f, **read_kwargs) for f in files]
    return pd.concat(frames, ignore_index=True)

df = load_and_concat('data/2026-*.csv', sep=';')

Частые ошибки

Мутабельный аргумент по умолчанию. Классическая ловушка — список или словарь в значении по умолчанию:

# Баг — список создаётся один раз и переиспользуется
def add_item(item, items=[]):
    items.append(item)
    return items

add_item('a')  # ['a']
add_item('b')  # ['a', 'b'] — сюрприз!

# Исправление — None как дефолт
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

Дефолтное значение вычисляется один раз при определении функции. Мутабельные объекты (list, dict, set) в дефолтах — почти всегда баг.

Забытый return. Если функция ничего не возвращает явно, результат — None:

def calc_tax(amount):
    tax = amount * 0.13
    # забыли return tax

result = calc_tax(100000)
print(result)  # None

Вопросы с собеседований

Что вернёт функция без return?None. В Python любая функция возвращает значение. Если return отсутствует или написан без выражения (return), функция вернёт None.

— **Что такое *args и kwargs?*args собирает позиционные аргументы в кортеж, **kwargs — именованные в словарь. Порядок в сигнатуре: обычные параметры, *args, именованные, **kwargs.

Чем опасен мутабельный аргумент по умолчанию? — Дефолт вычисляется один раз при определении функции. Если это список или словарь, он разделяется между всеми вызовами. Стандартное решение — None как дефолт и создание нового объекта внутри функции.

Чем lambda отличается от def? — lambda — одно выражение, без имени, без docstring. def — полноценная функция. Подробно — в гайде по lambda.

Что такое docstring и зачем он нужен? — Строковый литерал сразу после def. Доступен через help() и func.__doc__. Документирует, что делает функция, какие параметры принимает и что возвращает.

Потренировать Python-вопросы на практике можно в тренажёре Карьерника. А больше примеров вопросов — на отдельной странице. Разобраться с типами данных поможет гайд по типам данных, а с обработкой ошибок — гайд по try/except.

Открыть тренажёр в Telegram — вопросы по Python, pandas, SQL и аналитике. Бесплатно.

FAQ

Сколько аргументов может быть у функции в Python?

Формально ограничения нет — но CPython позволяет до 255 позиционных аргументов в вызове. На практике, если параметров больше 5-6, стоит передавать словарь или dataclass.

Можно ли вернуть из функции несколько значений?

Да. return a, b, c возвращает кортеж (a, b, c). При вызове значения распаковываются: x, y, z = func(). Это стандартный подход в Python.

Когда использовать функцию, а когда класс?

Если логика — чистое преобразование данных (вход → выход), достаточно функции. Класс нужен, когда есть состояние, которое меняется между вызовами. Для аналитических скриптов функций обычно хватает.