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

Как оцениваются аргументы при вызове функции? в Python.

Что происходит в момент вызова функции?

Вызов в языке Python вызывает вызываемый объект (функцию, метод и т.д.) с возможно пустой серией аргументов.

Вызываемым объектом в Python являются определяемые пользователем функции, встроенные функции, методы встроенных объектов, объекты класса, методы экземпляров класса и все объекты, имеющие метод __call__().

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

>>> def func(a, b):
...     print(a, b)
...
>>> func(1, 2,)
# 1 2

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

Если имеется N позиционных аргументов, то они помещаются в первые N слотов. Затем для каждого ключевого аргумента используется идентификатор для определения соответствующего слота (если идентификатор совпадает с именем первого формального параметра/аргумента, то используется первый слот и т. д.). Если слот уже заполнен, то возникает исключение TypeError. В противном случае значение аргумента помещается в слот, и заполняет его (даже в том случае, если выражение, переданное аргументу имеет значение None).

Когда все аргументы обработаны, начинается заполнение оставшихся пустых слотов соответствующим значением по умолчанию из определения функции. Значения по умолчанию вычисляются один раз при определении функции! Если остались есть незаполненные слоты, для которых не указано значение по умолчанию, возникает исключение TypeError. В противном случае список заполненных слотов используется в качестве списка аргументов для вызова.

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

Детали реализации CPython: реализация может предоставлять встроенные функции, позиционные параметры которых не имеют имен, даже если они используются для целей документации, и которые, следовательно, не могут быть переданы по ключевому слову. В CPython это относится к функциям, реализованным на C, которые используют PyArg_ParseTuple() для анализа своих аргументов.

Если имеется больше позиционных аргументов, чем имеется слотов формальных параметров/аргументов, то возникает исключение TypeError, если только не присутствует формальный параметр/аргумент, который использует синтаксис *identifier. В этом случае, этот формальный параметр/аргумент получает кортеж, содержащий избыточные позиционные аргументы или пустой кортеж, если не было лишних позиционных аргументов.

Если какой-либо ключевой аргумент не соответствует имени формального параметра/аргумента, то возникает исключение TypeError, если только не присутствует формальный параметр/аргумент, который использует синтаксис **identifier. В этом случае этот формальный параметр/аргумент получает словарь, содержащий избыточные ключевые аргументы (используя ключевые слова как ключи и значения аргументов как соответствующие значения), или (новый) пустой словарь, если не было лишних ключевых аргументов.

Если выражение синтаксиса *expression появляется в вызове функции, то выражение должно оцениваться как итерируемое. Элементы из этих итераций обрабатываются как дополнительные позиционные аргументы. Для вызова func(x1, x2, *y, x3, x4), если аргумент y вычисляется как последовательность y1,…, yM, то это эквивалентно вызову с M+4 позиционными аргументами x1, x2, y1,…, yM , x3, x4.

Следствием этого является то, что, хотя синтаксис *expression может появляться после явных ключевых аргументов, он обрабатывается перед ключевыми аргументами и любыми аргументами синтаксиса **expression:

>>> def func(a, b):
...     print(a, b)
...

>>> func(b=1, 2,)
#  File "<stdin>", line 1
# SyntaxError: positional argument follows keyword argument
>>> func(b=1, *(2,))
# 2 1
>>> func(a=1, *(2,))
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: f() got multiple values for keyword argument 'a'
>>> func(1, *(2,))
# 1 2

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

Если в вызове функции появляется синтаксис **expression, то expression должно оцениваться как словарь, содержимое которого обрабатывается как дополнительные ключевые аргументы. Если ключевое слово уже присутствует (как явный ключевой аргумент или как результат другой распаковки), то возникает исключение TypeError.

Формальные параметры/аргументы, использующие синтаксис *identifier или **identifier, не могут использоваться в качестве позиционных слотов аргументов или в качестве имен ключевых аргументов.

Вызов всегда возвращает некоторое значение, возможно, None, если только он не вызывает исключение. Как вычисляется это значение, зависит от типа вызываемого объекта:

  • Если это пользовательская функция: блок кода для функции выполняется, передавая ему список аргументов. Первое, что сделает блок кода, это привяжет формальные параметры к аргументам. Когда блок кода выполняет оператор return, он определяет возвращаемое значение вызова функции.

  • Если это встроенная функция или метод: результат зависит от интерпретатора.

  • Если это объект класса: возвращает новый экземпляр этого класса.

  • Если это метод экземпляра класса: вызывается соответствующая определяемая пользователем функция со списком аргументов, который на единицу длиннее, чем список аргументов в вызове: экземпляр становится первым аргументом self.

  • Если это экземпляр класса: класс должен определять метод __call__(). Эффект будет таким же, как если был вызван этот метод.