简短回答:两者都是基础,.__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)。顺便说一句,我骗了你,还有第三个基本操作:
好的,这仍然没有帮助。 type(obj).__call__(...) 仍然涉及属性访问和调用,所以这应该无限继续吗?问题是你最终会遇到 builtin 类型 - 通常是一个函数,object 或 type - 对它们而言,属性访问和函数调用 是基础。 p>
因此,当您调用自定义类的实例时,确实是通过其__call__ 方法实现的。但是它的__call__ 方法可能是一个普通的函数——可以直接调用。谜底结束。
类似__getattribute__ - 你可以将它提供给你的类define 属性访问,但是类本身从根本上实现属性访问(除非它有一个自定义的元类 )。
男人面前的帷幕
那么为什么连一个函数都有fred.__call__ 方法呢?好吧,这只是 Python 用来模糊内置类型和自定义类之间差异的烟雾和镜子。此方法存在于所有可调用对象上,但调用普通函数不必经过它 - 函数基本上是可调用的。
同样,所有对象都有obj.__getattribute__ 和obj.__class__,但对于内置类型,它只是公开基本操作而不是定义它。
小字
第一次声称 Python 有 2 个基本操作实际上完全是谎言。从技术上讲,所有 Python 运算符都有 C 级别的“基本”操作,通过方法公开以保持一致性,自定义类可以通过类似的方法重新定义这些操作。
但我告诉你的故事可能是真的,它减少了问题的中心:为什么.__call__() 和.__getattribute__() 不是无限递归。