Я пытаюсь получить концептуальное понимание природы функций Python и методов. Я добираюсь, который функционирует, на самом деле объекты, с методом, который называют, когда функция выполняется. Но то, что метод функционального объекта на самом деле другая функция?
Например:
def fred():
pass
Если я смотрю на dir(fred)
, Я вижу, что этому назвали атрибут __call__
. Но dir(fred.__call__)
также назвали атрибут __call__
. Делает fred.__call__.__call__
и так далее. Идентификаторы этой цепочки __call__
объекты предполагают, что они все отличны. Они, действительно возражает или этот некоторый прием низкого уровня интерпретатора?
Который более фундаментален: функции или методы объекта?
Что более фундаментально: функции или объектно-методы?
Думаю, лучшим ответом может быть "ни то, ни другое". Смотрите Execution model часть ссылки на Python, где она ссылается на "блоки". Вот что на самом деле выполняется. Вещь __call__
, на которую вы зацикливались в бесконечном поиске конца - это просто обёртка, которая знает как выполнить блок кода (смотрите вместо этого различные атрибуты func_xxx
вашей функции, где реальный байт-код хранится как func_code
).
Также актуальна секция определений Function definitions, которая ссылается на "объект функции [являющийся] (оберткой вокруг исполняемого кода функции)". Наконец, есть термин вызываемый, который также может быть ответом на "что более фундаментально?"
.Не совсем Python ответ, но на самом низком уровне процессор понимает только действия и переменные. Из этого мы экстраполируем функции, а из переменных и функций экстраполируем объекты. Поэтому с точки зрения низкоуровневого программирования я бы сказал, что более фундаментальным является функция.
Это не обязательно верно для Python в смысле Pythonic, и, наверное, является хорошим примером того, почему не всегда полезно глубоко вглядываться в реализацию языка как пользователя. :). Думать о функции как об объекте, безусловно, лучший ответ в самом языке Python.
Сначала я думал, что ваши вызовы отслеживаются в библиотеке Python, но метод .call обладает теми же самыми свойствами, что и любой другой метод. Таким образом, он рекурсивно исследует себя, я думаю, поиграв с python CLI несколько минут; я думаю, что это болезненный способ изучения архитектуры и, хотя это и не обязательно ошибка, свойство того, как Python обрабатывает объекты под обложками. :)
. Короткий ответ: оба фундаментальны, .__call__()
на функции - это просто виртуальный трюк.
Остальной ответ немного сложноват. Вам не обязательно понимать его, но я нахожу тему интересной. Предупреждаю, что представлю серию лжи, постепенно их исправляя.
На самом фундаментальном уровне можно сказать, что на Python есть всего 2 операции:
obj.attr
callable(args)
вызовы методов - obj.method(args)
- не являются фундаментальными. Они состоят из 2-х этапов: получение атрибута obj.method
(который дает вызываемый "связанный метод" объекта) и называя это с помощью args
.
Другие операторы определяются в терминах этих операторов. Например, x + y
пытается x.__add__(y)
, возвращаясь к другим подобным комбинациям, если это не работает.
Пока что хорошо. Но сами вызовы и доступ к атрибутам также определены в терминах obj.__call__(args)
и obj.__getattribute__(name)
?!?
.
Неужели это черепахи?!?
Хитрость в том, что операции над объектом определяются вызовом методов его типа : тип(obj).__call__(obj, args)
и тип(obj).__getattribute__(obj, name)
. Что BTW означает, что я солгал вам, и есть третья фундаментальная операция:
type(obj)
OK, это все равно не помогает. type(obj).__call__(...)
все еще включает в себя доступ к атрибуту и вызов, так что это должно продолжаться ad infinitum? Руб заключается в том, что в конечном итоге Вы попадаете в тип builtin - обычно это функция, объект
или тип
- и для них доступ к атрибутам и вызов функций являются фундаментальными.
Таким образом, при вызове экземпляра пользовательского класса, это действительно реализуется с помощью его метода __call__
. Однако его метод __call__
, скорее всего, является обычной функцией, которую можно вызывать напрямую. Конец тайны.
Аналогично с __getattribute__
- вы можете предоставить ему доступ к атрибуту define для вашего класса, но сам класс принципиально реализует доступ к атрибуту (если только он не имеет пользовательского metaclass).
Так почему же даже функция имеет метод fred.__call__
? Ну, это просто дым и зеркала, которые Python вытягивает, чтобы размыть разницу между встроенными типами и пользовательскими классами. Этот метод существует на всех вызываемых объектах, но вызов нормальной функции не обязательно должен через него проходить - функции в принципе вызываемые.
Аналогично, все объекты имеют obj.__getattribute__
и obj. __class__
, но для встроенных типов только раскрывает фундаментальные операции вместо , определяя его.
Первое утверждение, что на Python было 2 фундаментальных операции, на самом деле было полной ложью. Технически, все операторы на питоновском языке имеют "фундаментальную" операцию на уровне Си, выставленную для согласованности через метод, а пользовательские классы могут переопределить эти операции с помощью подобных методов.
Но история, которую я рассказал, могла быть правдой, и это уменьшает вопрос о её центре: почему .__call__()
и .__getattribute__()
не является бесконечной рекурссией.