Урок курса

Параметры по умолчанию и несколько возвращаемых значений

Python для новичков

В прошлом уроке функция принимала только обязательные параметры: если аргумент не передан, Python выдавал ошибку. Теперь разберём, как сделать параметр необязательным — задать ему значение прямо в сигнатуре, которое будет работать «по умолчанию», если аргумент при вызове не указан.

Параметры по умолчанию: синтаксис и логика работы

Параметр по умолчанию — это обычный параметр функции, которому сразу при объявлении присваивается значение. Синтаксис простой: после имени параметра ставится = и нужное значение прямо в строке def.

def greet(name, greeting='Привет'):
    print(f'{greeting}, {name}!')

Здесь name — обязательный параметр, а greeting — необязательный: у него уже есть значение 'Привет'.

Когда Python видит вызов функции, он ведёт себя по простому правилу: если аргумент для параметра передан — использует его, если нет — берёт значение по умолчанию.

greet('Алиса')                    # Привет, Алиса!
greet('Алиса', 'Добрый день')    # Добрый день, Алиса!

Первый вызов: greeting не передан, поэтому подставляется 'Привет'. Второй вызов: передан явный аргумент 'Добрый день', значение по умолчанию игнорируется.

Важно понимать: значение по умолчанию задаётся один раз при определении функции и остаётся неизменным между вызовами. Каждый раз, когда аргумент не передан, Python просто подставляет то самое значение, которое вы написали в сигнатуре.

Параметры по умолчанию особенно удобны, когда функция имеет «разумное» поведение в большинстве ситуаций, но иногда его нужно переопределить. Вместо того чтобы писать несколько функций или каждый раз передавать одно и то же значение, достаточно один раз задать его в сигнатуре.

Ошибка порядка параметров и правило расстановки

Python требует, чтобы все параметры со значениями по умолчанию шли после обязательных. Это не стилевое соглашение — это жёсткое синтаксическое правило. Если его нарушить, интерпретатор бросит SyntaxError ещё до запуска программы.

Посмотрим на неверный вариант:

def power(exp=2, base):  # SyntaxError!
    return base ** exp

Python не сможет разобрать эту сигнатуру и выдаст:

SyntaxError: non-default argument follows default argument

Почему это запрещено? Когда Python видит вызов power(3), ему нужно однозначно решить, какому параметру принадлежит аргумент 3. Если параметр со значением по умолчанию стоит первым, возникает неразрешимая неоднозначность: то ли 3 — это exp, то ли base, то ли Python должен «пропустить» exp и отдать 3 в base? Интерпретатор не угадывает — он просто запрещает такую сигнатуру.

Правильный порядок — сначала обязательные, потом необязательные:

def power(base, exp=2):
    return base ** exp

Теперь при вызове всё однозначно. Можно передать только base, и exp возьмёт значение по умолчанию. Можно передать оба аргумента, и значение по умолчанию будет проигнорировано:

print(power(3))     # 9  — exp = 2 (по умолчанию)
print(power(3, 3))  # 27 — exp = 3 (переопределён)

Оба вызова корректны. Первый — типичный сценарий, когда дефолтного значения достаточно. Второй — когда нужно переопределить поведение без создания новой функции.

Запомнить правило просто: смотришь на сигнатуру слева направо — сначала идут все параметры без =, потом все с =. Любое другое чередование — ошибка.

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

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

def safe_divide(a, b):
    if b == 0:
        return None, 'ошибка: деление на ноль'
    return a / b, 'ok'

Здесь return a / b, 'ok' — это не два оператора и не какой-то особый синтаксис. Python видит запятую между значениями и создаёт кортеж (a / b, 'ok'). Именно этот кортеж и возвращается из функции.

Чтобы убедиться в этом, можно поймать результат в одну переменную:

result = safe_divide(10, 2)
print(result)        # (5.0, 'ok')
print(type(result))  # <class 'tuple'>

Но обычно кортеж сразу распаковывают — это делает код читаемым и избавляет от обращений по индексу:

result, status = safe_divide(10, 2)
print(result)  # 5.0
print(status)  # ok

result, status = safe_divide(10, 0)
print(result)  # None
print(status)  # ошибка: деление на ноль

Распаковка работает ровно так же, как x, y = (1, 2) из урока про кортежи: Python берёт элементы по порядку и раскладывает их по переменным слева от =. Количество переменных слева должно совпадать с количеством значений в возвращаемом кортеже — иначе получите ValueError.

Этот паттерн — возврат результата вместе со статусом — встречается повсеместно. Функция сообщает не только «что получилось», но и «получилось ли вообще». Вызывающий код при этом сам решает, что делать с каждым из значений: проверить статус перед использованием результата, залогировать его или проигнорировать.

Важный момент: возвращаемые значения не обязаны быть одного типа. В safe_divide один из вариантов возвращает float и строку, другой — None и строку. Python не ограничивает вас в том, что именно класть в кортеж.

Процедура написания функции с опциональным параметром и множественным возвратом

Теперь объединим оба механизма в одну функцию. Разберём это пошагово на конкретном примере — напишем функцию, которая возводит число в степень, но при отрицательном показателе возвращает особый статус.

Шаг 1. Объявить сигнатуру: обязательные параметры идут первыми, необязательные — после.

def safe_power(base, exp=2):

base — обязательный, exp — необязательный со значением по умолчанию 2.

Шаг 2. В теле вычислить результат и сформировать статус.

def safe_power(base, exp=2):
    if exp < 0:
        result = None
        status = 'ошибка: отрицательный показатель не поддерживается'
    else:
        result = base ** exp
        status = 'ok'

Здесь два пути: отрицательный показатель даёт None и строку с описанием, неотрицательный — вычисленное значение и 'ok'.

Шаг 3. Написать return result, status — Python сам упакует значения в кортеж.

def safe_power(base, exp=2):
    if exp < 0:
        result = None
        status = 'ошибка: отрицательный показатель не поддерживается'
    else:
        result = base ** exp
        status = 'ok'
    return result, status

Один return в конце — он всегда возвращает кортеж из двух элементов, независимо от ветки.

Шаг 4. Вызвать функцию — с дефолтным параметром или с переопределённым.

# Только обязательный аргумент — exp берёт значение по умолчанию 2
r, s = safe_power(4)
print(r, s)  # 16 ok

# Оба аргумента — exp переопределён
r, s = safe_power(4, 3)
print(r, s)  # 64 ok

# Отрицательный показатель
r, s = safe_power(4, -1)
print(r, s)  # None ошибка: отрицательный показатель не поддерживается

Шаг 5. Использовать статус перед тем, как работать с результатом.

r, s = safe_power(4)
if s == 'ok':
    print(f'Результат: {r}')
else:
    print(f'Не удалось выполнить операцию: {s}')

Проверка статуса до использования результата — это и есть смысл паттерна «результат + статус»: вызывающий код знает, можно ли доверять значению r. Без такой проверки вы рискуете использовать None там, где ожидаете число, — и получить ошибку уже в другом месте программы, далеко от источника проблемы.

Распаковка работает по тому же правилу, что в предыдущей секции: количество переменных слева должно совпадать с количеством возвращаемых значений.

Вся процедура в сжатом виде: сначала обязательные параметры, потом необязательные; в теле — вычисление и статус; один return с несколькими значениями через запятую; при вызове — распаковка в нужное количество переменных; дальше — работа со статусом.

Попробуйте решить

Посмотрите на код:

def greet(name, greeting='Привет'):
    return greeting + ', ' + name

print(greet('Саша'))

Что выведет программа?

Продолжить с проверкой и прогрессом

Откройте интерактивный раннер с заданиями урока.

Перейти к интерактивному уроку
Параметры по умолчанию и return в Python