Python内部函数是否已编译?
因此,在CPython中,函数定义在解析时被编译成函数对象。但是内部功能呢?它们是在解析时被编译成函数对象,还是在每次调用函数时被编译(或解释)?内部函数是否会导致任何性能损失Python内部函数是否已编译?,python,Python,因此,在CPython中,函数定义在解析时被编译成函数对象。但是内部功能呢?它们是在解析时被编译成函数对象,还是在每次调用函数时被编译(或解释)?内部函数是否会导致任何性能损失 >>> import dis >>> def foo(): ... def bar(): ... print "stuff" ... return bar ... >>> b = foo() >>> dis.
>>> import dis
>>> def foo():
... def bar():
... print "stuff"
... return bar
...
>>> b = foo()
>>> dis.dis(foo)
2 0 LOAD_CONST 1 (<code object bar at 0x20bf738, file "<stdin>", line 2>)
3 MAKE_FUNCTION 0
6 STORE_FAST 0 (bar)
4 9 LOAD_FAST 0 (bar)
12 RETURN_VALUE
>>> dis.dis(b)
3 0 LOAD_CONST 1 ('stuff')
3 PRINT_ITEM
4 PRINT_NEWLINE
5 LOAD_CONST 0 (None)
8 RETURN_VALUE
因此,我们可以得出结论,它们是经过编译的。至于它们的性能特征,请使用它们。如果您开始出现性能问题,请配置文件。我知道这不是一个真正的答案,但它几乎从来都不重要,当它发生时,一般的答案并不能解决它。函数调用会产生一些开销,看起来内部函数就像函数一样。简单测试:函数的默认参数在定义时调用一次
>>> def foo():
... def bar(arg=count()):
... pass
... pass
...
>>> def count():
... print "defined"
...
>>> foo()
defined
>>> foo()
defined
是的:这是一个很小的(非常非常小的)性能损失。为了扩展nmichaels answer,内部函数是在编译时编译的,正如他猜测的那样,字节码保存在
foo.func\u code.co\u consts
中,并且可以使用操作码加载常量来访问它们,正如您在函数的反汇编中看到的那样
例如:
>>> def foo():
... def inner():
... pass
>>> print foo.func_code.co_consts
(None, <code object inner at 0x249c6c0, file "<ipython console>", line 2>)
>>def foo():
... def inner():
... 通过
>>>打印foo.func_code.co_常量
(无,<0x249c6c0处的代码对象内部,文件“ipython控制台”,第2行>)
给出一般解释-假设模块中有以下代码:
def outer(x=1):
def inner(y=2):
return x+y
当python通过compile()
解析文件时,上面的文本将转换为字节码,说明如何执行模块。在字节码模块中,有两个“代码对象”,一个用于outer()
的字节码,另一个用于inner()的字节码。请注意,我说的是代码对象,而不是函数-代码对象只包含函数使用的字节码,以及编译时可能知道的任何信息-例如outer()
的字节码,其中包含对inner()
字节码的引用
当模块实际加载时,通过评估与模块关联的代码对象,发生的一件事是为outer()
创建一个实际的“函数对象”,并存储在模块的outer
属性中。函数对象充当字节码和调用函数所需的所有上下文相关内容的集合(例如,它应该从哪些全局dict中提取,等等),这些在编译时是未知的。在某种程度上,代码对象是一个函数的模板,它是一个用于执行实际字节码并填充所有变量的模板
所有这些都不涉及internal()
-作为一个函数-每次您真正开始调用outer()
,都会为调用outer创建一个新的internal()
函数对象,它将已经创建的内部字节码对象绑定到一个全局变量列表,包括传递给外部调用的x
的值。您可以想象,这非常快,因为不需要解析,只需使用指向其他现有对象的指针填充一个快速结构。我对此有点晚,但作为这些彻底答案的一个小实验补充:您可以使用来验证是否创建了一个新对象:
In []: # inner version
def foo():
def bar():
return id(bar)
return bar()
foo(), foo()
Out[]: (4352951432, 4352952752)
实际数字可能不同,但它们的差异表明确实创建了两个不同的bar
实例
In []: # outer version
def bar():
return id(bar)
def foo():
return bar()
foo(), foo()
Out[]: (4352950952, 4352950952)
这一次,正如预期的那样,两个id
s是相同的
现在进行一些timeit
测量。内第一,外第二:
100000 loops, best of 3: 1.93 µs per loop
1000000 loops, best of 3: 1.25 µs per loop
因此,在我的机器上,内部版本似乎慢了50%(Python 2.7,IPython笔记本)。您是否有机会提供一个您所指的内部函数类型的示例?我倾向于说,如果你在另一个函数体中定义一个函数,性能会下降,主要是因为我希望它在外部函数的每次调用中都会被重新定义,但我想确保我完全理解你的问题。另外,timeit
模块将是测试它的一个很好的方法。非常清晰的答案与上述实验的结果一致。谢谢!有关详细信息,请参阅:
100000 loops, best of 3: 1.93 µs per loop
1000000 loops, best of 3: 1.25 µs per loop