# Функции в Python: def, return, аргументы, *args и **kwargs, lambda и аннотации типов

URL: https://skillaria.top/ru/guides/python/funkcii-python

Функция в Python — именованный блок кода, который объявляют словом def, вызывают по имени со скобками и из которого возвращают результат через return; без return функция возвращает None. Аргументы передаются по порядку или по имени, *args собирает лишние позиционные значения в кортеж, **kwargs — именованные в словарь, а lambda создаёт короткую анонимную функцию из одного выражения.

Функция в Python — это кусок кода с именем, который можно вызвать сколько угодно раз с разными входными данными. Её объявляют словом `def`, принимают значения через параметры и отдают результат через `return`. Функции в Python — главный способ превратить длинную «простыню» скрипта в набор понятных, проверяемых шагов.

## Как объявить функцию: def python

Объявление функции начинается с `def`, затем идут имя, параметры в скобках и двоеточие. Тело пишется с отступом в четыре пробела — отступ в Python не украшение, а часть синтаксиса.

```
def greet(name):
    """Возвращает приветствие для пользователя."""
    return f"Привет, {name}!"

print(greet("Анна"))   # Привет, Анна!
```

Строка в тройных кавычках сразу после заголовка — docstring, документация функции. Её показывает `help(greet)` и подсказки редактора. Имена функций по соглашению PEP 8 пишут в snake_case: `add_expense`, `get_rate`.

Строку `def greet(name):` называют сигнатурой функции: имя плюс список параметров (и аннотации, если они есть). По сигнатуре читатель понимает, как функцию вызывать, не заглядывая в тело.

## Что делает return в Python

`return` завершает функцию и отдаёт значение тому, кто её вызвал. Код после выполненного `return` не выполняется. Если `return` нет или он написан без значения, функция возвращает `None`.

```
def area(w, h):
    w * h          # результат вычислен и выброшен

print(area(3, 4))  # None

def area(w, h):
    return w * h

print(area(3, 4))  # 12
```

Забытый `return` — одна из самых частых ошибок новичков: функция «работает», но отдаёт `None`, и ошибка всплывает строчкой ниже. Вернуть можно и несколько значений — на самом деле это один кортеж, который удобно распаковать:

```
def min_max(numbers):
    return min(numbers), max(numbers)

low, high = min_max([4, 1, 9])
print(low, high)   # 1 9
```

Ранний `return` помогает избавиться от лишней вложенности: сначала отсекаем особые случаи, потом пишем основную логику.

```
def discount(price, is_member):
    if price  limit    # чтение глобальной — можно

def raise_limit():
    global limit
    limit = 2000             # без global создалась бы локальная переменная
```

На практике `global` лучше не использовать: функция, которая меняет внешнее состояние, плохо тестируется. Надёжнее принимать значение аргументом и возвращать результат.

## Вложенные функции и замыкания

Функцию можно объявить внутри другой функции. Вложенная функция видит переменные внешней даже после того, как внешняя завершилась, — это и есть замыкание. Для изменения такой переменной служит `nonlocal`.

```
def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

c = make_counter()
print(c(), c(), c())   # 1 2 3
```

Замыкания лежат в основе декораторов и фабрик функций.

## Функция — тоже объект

В Python функция — обычный объект: её можно положить в переменную, передать в другую функцию, вернуть из функции, хранить в словаре. У неё есть атрибуты: `__name__`, `__doc__`, `__defaults__`, `__annotations__`.

```
def double(x):
    return x * 2

f = double
print(f(5), f.__name__)                # 10 double
print(list(map(double, [1, 2, 3])))   # [2, 4, 6]
```

## Lambda в Python: анонимные функции

Lambda-функция — короткая функция без имени, тело которой состоит из одного выражения. Результат выражения возвращается автоматически, `return` писать нельзя.

```
square = lambda x: x ** 2
print(square(4))   # 16

expenses = [("Кофе", 250), ("Такси", 600), ("Обед", 450)]
expenses.sort(key=lambda e: e[1], reverse=True)
print(expenses)  # [('Такси', 600), ('Обед', 450), ('Кофе', 250)]

print(list(filter(lambda n: n % 2 == 0, range(10))))  # [0, 2, 4, 6, 8]
```

Лямбда-функции в Python уместны как короткий аргумент для `sorted`, `max`, `map`, `filter`. Присваивать лямбду переменной, как `square` выше, PEP 8 не рекомендует: если функции нужно имя, пишите `def` — у неё будет нормальный `__name__` в трейсбеке и место для docstring.

|  | def | lambda |
| --- | --- | --- |

| Имя | есть | нет (`<lambda>`) |

| Тело | любое число инструкций | одно выражение |

| return | явный | неявный |

| Docstring, аннотации | да | нет |

| Где уместна | везде | короткий key или колбэк |

## Аннотации типов: подсказки, а не проверки

Аннотации описывают ожидаемые типы параметров и результата. Сам Python при запуске их не проверяет: функция с `x: int` спокойно примет строку. Проверяют аннотации внешние инструменты — mypy, pyright, ty — и редактор, который подсвечивает ошибку ещё до запуска.

```
def add_expense(title: str, amount: float, category: str = "прочее") -> dict[str, object]:
    return {"title": title, "amount": amount, "category": category}

def area(w: float, h: float) -> float:
    w * h   # mypy: Missing return statement
```

Запустить проверку без установки можно через `uvx mypy .`. Пример выше показывает пользу аннотаций: забытый `return` при `-> float` анализатор ловит сразу.

С Python 3.12 (PEP 695) обобщённые функции пишутся без `TypeVar`: параметр типа указывается в квадратных скобках, а псевдоним типа объявляется словом `type`.

```
def first[T](xs: list[T]) -> T:
    return xs[0]

type Money = int | float
```

В Python 3.14 (PEP 649) аннотации стали ленивыми: они вычисляются при первом обращении, поэтому ссылаться на класс, объявленный ниже, можно без кавычек, а `from __future__ import annotations` больше не нужен.

## «Не передали» или «передали None»: sentinel в 3.15

Значение `None` по умолчанию не позволяет отличить «аргумент не передали» от «осознанно передали None». Годами это решали самодельной заглушкой:

```
_MISSING = object()

def get_rate(currency, default=_MISSING):
    rates = {"USD": 90.5}
    if currency in rates:
        return rates[currency]
    if default is _MISSING:
        raise KeyError(currency)
    return default
```

В Python 3.15 появилась встроенная функция `sentinel` (PEP 661): `MISSING = sentinel("MISSING")` создаёт уникальный маркер с понятным repr, который при копировании остаётся самим собой, сериализуется через pickle и годится в аннотацию вида `float | MISSING`. На старых версиях продолжает работать приём с `object()`.

## Типичные ошибки с функциями

- **Забыли return** — функция возвращает `None`.
- **Забыли скобки** — `print(greet)` выведет объект функции, а не результат вызова.
- **Изменяемое значение по умолчанию** — общий список на все вызовы.
- **Неверное число аргументов** — `TypeError: greet() missing 1 required positional argument: 'name'`.
- **Имя совпадает со встроенным** — функция `list` или `sum` затенит встроенную.

## Итог

Функции в Python объявляются через `def`, возвращают результат через `return` и принимают аргументы по порядку или по имени. `*args` и `**kwargs` дают гибкость, `/` и `*` — строгость вызова, lambda — краткость там, где нужна одноразовая функция, а аннотации типов вместе с mypy ловят ошибки до запуска. В курсе по Python эту тему разбирают на практике: трекер расходов разбивается на функции с аннотациями и проверяется mypy.

## FAQ

**Чем параметр отличается от аргумента?**

Параметр — имя в объявлении функции, аргумент — конкретное значение, переданное при вызове. В def greet(name) name — параметр, в greet("Анна") строка "Анна" — аргумент.

**Можно ли вызвать функцию до её объявления?**

На уровне модуля — нет: строка def должна выполниться раньше вызова, иначе будет NameError. Но внутри другой функции можно вызывать функцию, объявленную ниже, если вызов произойдёт после её объявления.

**Сколько значений может вернуть функция?**

Формально одно, но этим значением может быть кортеж: return a, b возвращает кортеж из двух элементов, который распаковывают как x, y = f().

**Обязательно ли писать аннотации типов?**

Нет, Python работает и без них. Но в коде, который читают другие люди, аннотации заметно упрощают поддержку и позволяют mypy или pyright находить ошибки до запуска.

**Можно ли в lambda написать if?**

Можно только условное выражение: lambda x: "чёт" if x % 2 == 0 else "нечет". Инструкции, циклы и присваивания внутри lambda недопустимы.


---
Source: Skillaria.Top (https://skillaria.top)
