Сообщить об ошибке.

Ограничение способов передачи аргументов в функцию Python

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

Определение функции может выглядеть так (доступно c версии Python3.8):

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |                |              |
        |         позиционные или       |
        |         по ключу               - только по ключевому слову
        |
         -- только по позиции

где символы / и * являются НЕобязательными. Эти символы указывают тип аргумента в зависимости от того, как они могут быть переданы в функцию:

  • только по позиции,
  • по позиции или по ключевому слову
  • только по ключевому слову.

Аргументы, которые передаются ключевому слову также называются именованными аргументами (ключевые аргументы).

ВНИМАНИЕ: ограничение способов передачи аргументов, символами / и * в виде def f(... /, ... *, ...), стало доступно с версии Python3.8 и выше.

Позиционные или ключевые аргументы.

Если в определении функции нет специальных символов / и *, то аргументы могут быть переданы функции по позиции или по ключевому слову.

Только позиционные параметры .

При определении параметров функции можно указать область, в которой требуется передача только позиционных аргументов. Если указана такая область, то передать ключевое слово (имя аргумента), в этой области, при вызове функции становиться невозможным. Перед символом / (косая черта) могут располагаться только позиционные аргументы. Символ '/' используются для логического разделения только позиционных аргументов от остальных передаваемых аргументов в функцию. Если в определении функции нет символа /, значит и нет только позиционных аргументов.

Параметры, следующие за /, могут быть позиционными или ключевыми аргументами или только ключевыми аргументами.

Только ключевые аргументы.

Чтобы определить область параметров функции, которые будут принимать только ключевые аргументы, нужно поместить их после маркера * - который означает список аргументов только для ключевых слов.

Примеры способов передачи аргументов функций.

Напоминаем, что эта функциональность доступна с версии Python3.8 и выше.

Рассмотрим следующие примеры определений функций с маркерами /(только позиционные) и * (только ключевые):

>>> def standard_arg(arg):
...     print(arg)
...
>>> def pos_only_arg(arg, /):
...     print(arg)
...
>>> def kwd_only_arg(*, arg):
...     print(arg)
...
>>> def combined_example(pos_only, /, standard, *, kwd_only):
...     print(pos_only, standard, kwd_only)

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

>>> standard_arg(2)
# 2

>>> standard_arg(arg=2)
# 2

Вторая функция pos_only_arg, ограничена использованием только позиционных параметров, поскольку / в определении функции есть:

>>> pos_only_arg(1)
# 1

>>> pos_only_arg(arg=1)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: pos_only_arg() got an unexpected keyword argument 'arg'

Третья функция kwd_only_args, допускает только ключевые аргументы, как указано * в определении функции:

>>> kwd_only_arg(3)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: kwd_only_arg() takes 0 positional arguments but 1 was given

>>> kwd_only_arg(arg=3)
# 3

И последний использует все три соглашения о вызовах в одном и том же определении функции:

>>> combined_example(1, 2, 3)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: combined_example() takes 2 positional arguments but 3 were given

>>> combined_example(1, 2, kwd_only=3)
# 1 2 3
>>> combined_example(1, standard=2, kwd_only=3)
# 1 2 3

>>> combined_example(pos_only=1, standard=2, kwd_only=3)
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: combined_example() got an unexpected keyword argument 'pos_only'

Наконец, рассмотрим определение этой функции, которое имеет потенциальную коллизию между позиционным аргументом name и **kwds которое имеет name в качестве ключа:

def foo(name, **kwds):
    return 'name' in kwds

Функция никогда не вернет True, поскольку ключевое слово name всегда будет привязываться к первому параметру. Например:

>>> foo(1, **{'name': 2})
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: foo() got multiple values for argument 'name'
>>>

Но, используя / (только позиционные аргументы), это возможно, так как он допускает name в качестве позиционного аргумента и name в качестве ключа в аргументах ключевое слово:

def foo(name, /, **kwds):
    return 'name' in kwds

foo(1, **{'name': 2})
# True

Другими словами имена только позиционных параметров могут использоваться в '**kwds' без двусмысленности.

В качестве руководства к использованию.

Рассмотренные варианты показывают, как использовать параметры / и * в определении функции:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):

Хорошие практики использования специальных параметров / и * в определении функции:

  • Используйте только позиционные параметры, если вы хотите, чтобы имя параметров было недоступно для пользователя. Это полезно, когда имена параметров не имеют реального значения, если вы хотите установить порядок аргументов при вызове функции или если вам нужно взять некоторые позиционные параметры и произвольные ключевые слова.

  • Используйте только ключевые аргументы, когда имена параметров имеют значение, а определение функции более понятно, если явно указывать имена или если вы хотите, чтобы пользователи не полагались на позицию передаваемого аргумента.

  • Для API используйте только позиционное, чтобы предотвратить нарушение API, если имя параметра будет изменено в будущем.