Что более фундаментально: функции Python или объектные методы Python?

Я пытаюсь получить концептуальное представление о природе функций и методов Python. Я получаю, что функции фактически являются объектами, с методом, который вызывается при выполнении функции. Но является ли этот метод объекта-объекта фактически другой функцией?

Например:

def fred():
    pass

Если я смотрю на dir(fred), я вижу, что у него есть атрибут с именем __call__. Но dir(fred.__call__) также имеет атрибут с именем __call__. Так что fred.__call__.__call__ и так далее. Иды этой цепочки объектов __call__ предполагают, что они все различны. Действительно ли они объективны или это какой-то трюк низкого уровня интерпретатора?

Что более важно: функции или объектные методы?

Ответ 1

Что более фундаментально: функции или объектных методов?

Я думаю, лучший ответ может быть "ни". См. "Модель выполнения" в ссылке на Python, где она относится к "блокам". Это то, что фактически выполняется. Вещь __call__, которую вы повесили в бесконечном поиске конца, - это просто оболочка, которая знает, как выполнить блок кода (см. Различные атрибуты func_xxx вашей функции, а фактический байт-код будет сохранен как func_code).

Также уместно, Определения функций, который ссылается на "объект функции [являющийся] (обертка вокруг исполняемого кода для функция)". Наконец, существует термин вызываемый, который также может быть ответом на "что более фундаментально?"

Ответ 2

Короткий ответ: оба являются фундаментальными, .__call__() для функций - всего лишь виртуальный трюк.


Остальная часть этого ответа немного сложна. Вы не должны это понимать, но я нахожу тему интересной. Будьте предупреждены, что я представлю серию лжи, постепенно их исправляя.

Длинный ответ

На самом фундаментальном уровне можно сказать, что Python имеет всего 2 операции:

  • доступ к атрибутам: obj.attr
  • вызов функции: callable(args)

Вызов метода - obj.method(args) - не является фундаментальным. Они состоят из двух шагов: выбор атрибута obj.method (который дает объект вызываемого объекта "связанный метод" ) и вызов этого с помощью args.

Другие операторы определяются в терминах них. Например. x + y пытается x.__add__(y), возвращаясь к другим подобным комбинациям, если это не работает.

Бесконечно длинный ответ?

Пока все хорошо. Но вызов и атрибут доступа также определяются в терминах obj.__call__(args) и obj.__getattribute__(name)?!?
Это черепахи полностью вниз?!?

Фокус в том, что операции над объектом определяются вызывающими методами его типа: type(obj).__call__(obj, args) и type(obj).__getattribute__(obj, name). Какой BTW означает, что я лгал вам, и есть третья фундаментальная операция:

  • получение типа объекта: type(obj)

ОК, это все еще не помогает. type(obj).__call__(...) все еще включает в себя доступ к атрибуту и ​​вызов, поэтому это должно продолжаться до бесконечности? В результате вы попадаете в встроенный тип - обычно это функция, object или type - и для них основные функции имеют доступ к атрибутам и функциям.

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

Аналогично о __getattribute__ - вы можете предоставить ему определение доступа к атрибуту для вашего класса, но сам класс реализует доступ к атрибутам принципиально (если у него нет пользовательского метакласса).

Занавес перед человеком

Итак, почему даже функция имеет метод fred.__call__? Ну, это просто дым и зеркала, которые Python тянет, чтобы размыть разницу между встроенными типами и пользовательскими классами. Этот метод существует для всех вызываемых объектов, но вызов нормальной функции не должен проходить через него - функции в принципе могут быть вызваны.

Аналогично, все объекты имеют obj.__getattribute__ и obj.__class__, но для встроенных типов он просто предоставляет фундаментальные операции вместо определения.

Малая печать

Первое утверждение о том, что Python имеет 2 фундаментальных операции, было фактически полной ложью. Технически все операторы Python имеют "фундаментальную" операцию на уровне C, выставленную для согласованности с помощью метода, а пользовательские классы могут переопределять эти операции с помощью аналогичных методов.

Но рассказ, который я вам сказал, может быть правдой, и это сводит вопрос к его центру: почему .__call__() и .__getattribute__() не являются бесконечной рекурсией.

Ответ 3

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

Это не обязательно верно для Python в питоническом смысле и, вероятно, является хорошим примером того, почему не всегда полезно глубже вникать в реализацию языка в качестве пользователя.:) Думая о функции как объекте, безусловно, лучший ответ в самом Python.

Сначала я думал, что ваши звонки отслеживаются в библиотеке Python, но метод .call имеет те же свойства, что и любой другой метод. Таким образом, он рекурсивно исследует себя, я думаю, играл с CLI python в течение нескольких минут; Я думаю, что это болезненный способ изучения архитектуры и, хотя и не обязательно ошибка, как свойство Python обрабатывает объекты под обложками.:)