Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Больше об именованных аргументах Python: Функции

В этом уроке мы разберем, как получать произвольное количество именованных аргументов, как передавать их в виде коллекции и как объявлять keyword-only аргументы.

Получение именованных аргументов в виде словаря

Позиционные аргументы можно получать в виде *args, причем в произвольном количестве. Для именованных аргументов тоже существует подобная возможность. Только они получаются в виде словаря, что позволяет сохранить имена аргументов в ключах:

def g(**kwargs):
    return kwargs

g(x=1, y=2, z=None)
# {'x': 1, 'y': 2, 'z': None}

По соглашению аргумент, который получает подобный словарь, принято называть kwargs — от словосочетания keyword arguments.

Порядок вызовов смешанных аргументов

Поэкспериментируем с разными комбинациями аргументов, которые можно передавать функциям:

def f(*args, **kwargs):
    return (args, kwargs)

f(1, 2, 3, 4, kx='a', ky='b', kz='c')
# ((1, 2, 3, 4), {'kx': 'a', 'ky': 'b', 'kz': 'c'})

Заметим, что *args всегда указывается перед **kwargs, иначе будет ошибка:

def f(**kwargs, *args):
    return (kwargs, args)

f(1, kx='a') # SyntaxError: invalid syntax

Также при объявлении функций можно комбинировать позиционные аргументы, значения по умолчанию, *args и **kwargs одновременно. При использовании обычных позиционных аргументов их следует добавлять в начало перед аргументом *args:

def f(x, *args, **kwargs):
    return (x, args, kwargs)

f(1, 2, 3, kx='a', ky='b')
# (1, (2, 3), {'kx': 'a', 'ky': 'b'})

Аргументы со значением по умолчанию следует добавлять после аргумента *args, но перед аргументом **kwargs:

def f(*args, ky=42, **kwargs):
    return (args, ky, kwargs)

f(1, 2, 3, 4, kz='c')
# ((1, 2, 3, 4), 42, {'kz': 'c'})

f(1, 2, 3, 4, ky='b', kz='c')
# ((1, 2, 3, 4), 'b', {'kz': 'c'})

Аргумент *args в определении функции пишется после всех обычных позиционных аргументов, но перед первым аргументом со значением по умолчанию. А **kwargs пишется в самом конце после последнего аргумента со значением по умолчанию.

Согласно этому правилу у нас идет следующий порядок расстановки аргументов:

  1. Обычные позиционные аргументы
  2. Аргумент *args
  3. Аргументы со значением по умолчанию
  4. Аргумент **kwargs
def f(x, y, *args, kx=None, ky=42, **kwargs):
    return (x, y, args, kx, ky, kwargs)

f(1, 2, 3, 4, kx='a', ky='b', kz='c')
# (1, 2, (3, 4), 'a', 'b', {'kz': 'c'})

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

Передача именованных аргументов с помощью словаря

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

def coords(x, y):
    return (x, y)

coords(x=1, **{'y': 2})
# (1, 2)

Здесь указан обычный именованный аргумент, а другой завернут в словарь.

Попробуем вызвать функцию с двумя наборами аргументов: для позиционных и для именованных:

def f(x, y, *args, kx=None, ky=42, **kwargs):
    return (x, y, args, kx, ky, kwargs)

positional = (2, 3)
named = dict(ky='b', kz='c')
f(1, *positional, 4, kx='a', **named)
# (1, 2, (3, 4), 'a', 'b', {'kz': 'c'})

В этом примере мы не написали литерал, а вместо этого вызвали функцию dict с несколькими именованными аргументами. Так словарь еще больше похож на сохраненный набор аргументов.

При подстановке аргументов разворачивающиеся наборы аргументов вроде *positional и **named можно указывать вперемешку с аргументами соответствующего типа: *positional — с позиционными, **named — с именованными. При этом все именованные аргументы должны идти после всех позиционных.

Keyword-only аргументы

В Python 3 добавили возможность пометить именованные аргументы функции так, чтобы вызывать функцию можно было только через передачу этих аргументов по именам. Такие аргументы называются keyword-only и их нельзя передать в функцию в виде позиционных. Выглядит функция с подобными аргументами так:

def open_file(name, *, writable=False, binary=False):
    

f1 = open_file('foo.txt', writable=True)
f2 = open_file('bar.bin', binary=True)
f3 = open_file('raw.dat', True, True)
# TypeError: open_file() takes 1 positional argument but 3 were given

Здесь * выступает разделителем — отделяет обычные аргументы от строго именованных. Такой разделитель можно использовать только один раз в одном определении. Еще его нельзя применять в функциях с *args. Но можно объявлять функции, у которых будут только строго именованные аргументы. Для этого нужно поставить звездочку в самом начале перечня аргументов.

Этот пример демонстрирует подход к описанию аргументов. Первый аргумент — имя файла, который будет открыт. Оно всегда присутствует и связано по смыслу с именем функции. Поэтому этот аргумент можно не именовать. А writable и binary — необязательные аргументы, которые получают значения True/False. Поэтому опции и объявлены так, что могут быть указаны только явно.

Когда мы используем keyword-only аргументы вместе с именованными аргументами (**kwargs), возникает проблема. Именованные аргументы могут перехватить значения, которые должны были быть переданы как keyword-only аргументы. В итоге это может привести к ошибкам в работе функции.

Рассмотрим следующий пример:

def my_func(a, *, b=None, **kwargs):
    print(a, b, kwargs)

my_func(1, b=2, c=3)
# 1 2 {'c': 3}

В данном примере аргумент a принимает значение 1, а keyword-only аргумент b — значение 2. При этом аргумент c передается в **kwargs, что может привести к ошибке, если c не должен был передаваться этой функции.

Поэтому рекомендуется использовать либо keyword-only аргументы, либо **kwargs, но не оба вместе. Это поможет избежать неожиданного поведения и ошибок в работе функции.

Порядок аргументов при вызове функций

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

def f(x, y, *args, kx=None, ky=42, **kwargs):
    return (x, y, args, kx, ky, kwargs)

foo = [1, 2, 3]
bar = "abc"
f(kx=42, *foo, ky=100, *bar)
# (1, 2, (3, 'a', 'b', 'c'), 42, 100, {})

Еще одна особенность заключается в том, что мы не можем одновременно указать аргумент x по имени и при этом развернуть набор параметров для функции с сигнатурой вида f(x, *args). То есть мы не сможем сделать так: f(*foo, x=42).

Выводы

В этом уроке мы изучили несколько важных концепций, которые позволяют управлять аргументами функций в Python. Мы рассмотрели способ получения именованных аргументов в виде словаря, порядок вызовов смешанных аргументов, способ передачи именованных аргументов с помощью словаря и работу с keyword-only аргументами.

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


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Об обучении на Хекслете

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 6 300 ₽ в месяц
Разработка веб-приложений на Django
10 месяцев
с нуля
Старт 18 апреля

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»