【问题标题】:Are Python "function objects" functions?Python“函数对象”是函数吗?
【发布时间】:2017-06-09 13:21:33
【问题描述】:

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

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

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

我的意思是功能:

def f(a):
    pass

【问题讨论】:

  • 什么是“普通函数”?
  • 请举例说明你认为什么是函数对象和函数。
  • 这就像问Python中的普通intint对象有什么区别......那里不是普通的ints.
  • @MikeMüller 我编辑了问题,解释了我对函数对象和函数的含义。感谢您提出更清晰的问题。

标签: python python-2.7 function callable descriptor


【解决方案1】:

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

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

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

【讨论】:

  • 在 OP 澄清他的问题后,这似乎无关紧要。
  • “函数”对象是否具有属性“set”?属性“get”呢?你没有回答我的问题。
【解决方案2】:

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

略长的版本:(walloftext

关于你的问题的完整答案是在 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[] difinition 处理的:

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 可调用对象如何在后台工作的文章,以及我自己通过 python source code 进行的一些挖掘

【讨论】:

  • 感谢您的有趣回答。实际上,我想知道更多关于高级语言的功能,例如为什么“函数”对象没有属性“set”和“get”。但我发现你在回答中提供的信息也很有趣。
  • 函数确实实现了__get__,它返回文本“
  • 使用 getter 和 setter 来应用于类的单个属性而不是类本身更为常见。函数也不是从基对象类继承的,目的是如果你想要自定义行为,你可以用类来实现它。从技术上讲,您可以创建自己的函数类,但这会非常缓慢且相当复杂。通过不从基对象类继承,python 可以运行它的 c 代码来管理代码对象的调用
猜你喜欢
  • 2014-09-02
  • 2014-10-07
  • 1970-01-01
  • 1970-01-01
  • 2014-05-27
  • 1970-01-01
  • 1970-01-01
  • 2012-02-06
  • 2014-02-01
相关资源
最近更新 更多