Python "function objects" 是函数吗?

Are Python "function objects" functions?

我们都知道函数也是对象。但是函数对象与函数相比如何呢?函数对象和函数有什么区别?

通过函数对象,我的意思是 g 如此定义:

class G(object):
    def __call__(self, a):
        pass
g = G()

按功能我的意思是:

def f(a):
    pass

Python 在您使用 def 语句或使用 lambda 表达式时为您创建函数对象:

>>> def foo(): pass
... 
>>> foo
<function foo at 0x106aafd70>
>>> lambda: None
<function <lambda> at 0x106d90668>

因此,每当您在 python 中定义一个函数时,都会创建一个这样的对象。 没有简单的函数定义方式。

TL;DR 可以调用任何实现 __call__ 的对象,例如:函数、自定义 classes 等。

稍微长一点的版本: (walloft分机)

您关于有何不同的问题的完整答案在于 python 虚拟机的实现,因此我们必须深入了解 python。首先是代码对象的概念。 Python 将您输入的任何内容解析为它自己的内部语言,这种语言在所有平台上都相同,称为字节码。一个非常直观的表示是在导入您编写的自定义库后获得 .pyc 文件。这些是 python 虚拟机的原始指令。忽略这些指令是如何从您的源代码创建的,它们随后由 Python/ceval.c 中的 PyEval_EvalFrameEx 执行。源代码有点像野兽,但最终就像一个简单的处理器一样工作,其中抽象了一些复杂的位。字节码是该处理器的汇编语言。特别是这个 "processor" 的 "opcodes" 之一是(恰当地命名)CALL_FUNCTION。回调经过多次调用,最终到达 PyObject_Call()。此函数采用指向 PyObject 的指针并从其类型中提取 tp_call 属性并直接调用它(从技术上讲,它首先检查它是否存在):

...
call = func->ob_type->tp_call //func is an arg of PyObject_Call() and is a pointer to a PyObject
...
result = (*call)(func, arg, kw);

任何实现 __call__ 的对象都被赋予一个 tp_call 属性,并带有指向实际函数的指针。我相信这是由 Objects/typeobject.c:

slotdefs[] 定义处理的
FLSLOT("__call__", tp_call, slot_tp_call, (wrapperfunc)wrap_call,
       "__call__($self, /, *args, **kwargs)\n--\n\nCall self as a function.",
       PyWrapperFlag_KEYWORDS)

函数的 __call__ 方法本身在 cpython 实现中定义,它定义了 python VM 应该如何开始执行该函数的字节码以及数据应该如何返回(如果有)。当你给任意 class 一个 __call__ 方法时,该属性是一个函数对象,它再次引用回 __call__ 的 cpython 实现。因此,当您调用 "normal" 函数时,会引用 foo.__call__。当您调用可调用的 class 时,self.__call__ 等同于 foo 并且调用的实际 cpython 引用是 self.__call__.im_func.__call__.

免责声明

对我来说,这是一段进入未知领域的旅程,我完全有可能歪曲了实施的一些细节。我主要取自this blog post on how python callables work under the hood, and some digging of my own through the python source code