2024-04-20 08:46:00 发布
网友
我们都知道函数也是对象。但是函数对象与函数相比如何呢?函数对象和函数之间有什么区别?你知道吗
所谓函数对象,我指的是这样定义的g:
class G(object): def __call__(self, a): pass g = G()
我所说的函数是指:
def f(a): pass
使用def语句或lambda表达式时,Python会为您创建函数对象:
>>> def foo(): pass ... >>> foo <function foo at 0x106aafd70> >>> lambda: None <function <lambda> at 0x106d90668>
因此,无论何时在python中定义函数,都会创建这样的对象。函数定义没有简单的方法。你知道吗
TL;DR任何实现__call__的对象都可以被调用,例如:函数、自定义类等。。你知道吗
__call__
稍长版本:(w全部oft分机)
对于您提出的关于有什么不同的问题,完整的答案在python虚拟机的实现中,因此我们必须看看python的幕后黑手。首先是代码对象的概念。Python会将您所抛出的任何东西解析为它自己的内部语言,这种语言在所有被称为字节码的平台上都是相同的。一个非常直观的表示是,当您在导入您编写的自定义库之后获得一个.pyc文件时。这些是pythonvm的原始指令。忽略这些指令是如何从源代码创建的,然后由Python/ceval.c中的PyEval_EvalFrameEx执行。源代码有点像野兽,但最终就像一个简单的处理器,将一些复杂的部分抽象掉。字节码是这个处理器的汇编语言。特别地,这个“处理器”的“操作码”之一是(恰当地命名)CALL_FUNCTION。回调经过多次调用,最终到达PyObject_Call()。此函数获取指向PyObject的指针,并从其类型中提取^{}属性并直接调用它(从技术上讲,它会检查它是否首先在那里):
PyEval_EvalFrameEx
CALL_FUNCTION
PyObject_Call()
PyObject
... 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[]定义处理的:
tp_call
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__。你知道吗
foo.__call__
self.__call__
foo
self.__call__.im_func.__call__
免责声明
对我来说,这是一次进入未知领域的旅程,我完全有可能曲解了实现的一些细节。我主要从this的博客文章中获取关于python callables如何在引擎盖下工作的信息,并通过python source code挖掘了一些我自己的信息
使用def语句或lambda表达式时,Python会为您创建函数对象:
因此,无论何时在python中定义函数,都会创建这样的对象。函数定义没有简单的方法。你知道吗
TL;DR任何实现
__call__
的对象都可以被调用,例如:函数、自定义类等。。你知道吗稍长版本:(w全部oft分机)
对于您提出的关于有什么不同的问题,完整的答案在python虚拟机的实现中,因此我们必须看看python的幕后黑手。首先是代码对象的概念。Python会将您所抛出的任何东西解析为它自己的内部语言,这种语言在所有被称为字节码的平台上都是相同的。一个非常直观的表示是,当您在导入您编写的自定义库之后获得一个.pyc文件时。这些是pythonvm的原始指令。忽略这些指令是如何从源代码创建的,然后由Python/ceval.c中的} 属性并直接调用它(从技术上讲,它会检查它是否首先在那里):
PyEval_EvalFrameEx
执行。源代码有点像野兽,但最终就像一个简单的处理器,将一些复杂的部分抽象掉。字节码是这个处理器的汇编语言。特别地,这个“处理器”的“操作码”之一是(恰当地命名)CALL_FUNCTION
。回调经过多次调用,最终到达PyObject_Call()
。此函数获取指向PyObject
的指针,并从其类型中提取^{任何实现
__call__
的对象都被赋予一个tp_call
属性,并带有指向实际函数的指针。我相信这是由Objects/typeobject.c中的slotdefs[]
定义处理的:函数的
__call__
方法本身是在cpython实现中定义的,它定义了python VM应该如何开始执行该函数的字节码,以及应该如何返回数据(如果有的话)。当您给一个任意类一个__call__
方法时,属性是一个函数对象,它再次引用了__call__
的cpython实现。因此,当您调用一个“普通”函数时foo.__call__
被引用。调用可调用类时,self.__call__
等价于foo
,实际调用的cpython引用是self.__call__.im_func.__call__
。你知道吗免责声明
对我来说,这是一次进入未知领域的旅程,我完全有可能曲解了实现的一些细节。我主要从this的博客文章中获取关于python callables如何在引擎盖下工作的信息,并通过python source code挖掘了一些我自己的信息
相关问题 更多 >
编程相关推荐