Python“函数对象”是否具有功能?

时间:2017-01-24 16:28:02

标签: python python-2.7 function callable descriptor

我们都知道函数也是对象。但是函数对象如何与函数进行比较?功能对象和功能有什么区别?

按功能对象我的意思是如此定义:

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

按功能我的意思是:

def f(a):
    pass

2 个答案:

答案 0 :(得分:2)

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

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

因此,无论何时在python中定义函数,都会创建这样的对象。 没有简单的函数定义方式。

答案 1 :(得分:1)

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

版本略长:(w all o f t ext

你的问题的完整答案是关于python虚拟机的实现有什么区别,所以我们必须看看底层的python。首先是代码对象的概念。 Python将你投入的任何内容解析为它自己的内部语言,在所有称为字节码的平台上都是相同的。对此进行非常直观的重新解释是在导入您编写的自定义库后获得.pyc文件。这些是python VM的原始指令。忽略这些指令是如何从源代码创建的,然后由 Python / ceval.c 中的PyEval_EvalFrameEx执行。源代码有点野兽,但最终的工作方式就像一个简单的处理器,其中一些复杂的部分被抽象掉了。字节码是此处理器的汇编语言。特别是这个“处理器”的一个“操作码”是(恰当地命名)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应该如何开始执行该函数的字节码以及如何返回数据(如果有的话)。当您为任意类提供__call__方法时,该属性是一个函数对象,它再次引用回__call__的cpython实现。因此,当您调用“正常”函数时,foo.__call__被引用。当你调用一个可调用的类时,self.__call__相当于foo,而实际的cpython引用是self.__call__.im_func.__call__

<强>声明

这对我来说是一段有点未知的水域,我完全有可能歪曲了实施的一些细节。我主要从this博客文章中了解了python callables如何工作,以及通过python挖掘我自己的source code