Python是否会优化掉仅用作返回值的变量?


106

以下两个代码段之间是否有最终区别?首先为函数中的变量分配一个值,然后返回该变量。第二个函数只是直接返回值。

Python是否将它们转换为等效的字节码?是其中之一吗?

情况1

def func():
    a = 42
    return a

情况2

def func():
    return 42

5
如果dis.dis(..)两者都使用,您会发现两者之间存在差异,所以可以。但是在大多数实际应用中,与功能中的处理延迟相比,此开销并不多。
Willem Van Onsem '17

4
有两种可能:(a)您将在紧密循环中多次调用此函数(即,至少一百万次)。在这种情况下,您根本不应该调用Python函数,而应该使用numpy库之类的东西对循环进行矢量化处理。(b)您不会多次调用此函数。在那种情况下,这些功能之间的速度差异太小而不必担心。
亚瑟塔卡

Answers:


138

不,不是

CPython字节码的编译仅通过小型的猫眼优化器传递,该优化器仅用于基本优化(有关这些优化的更多信息,请参见测试套件中的test_peepholer.py)。

要查看实际发生的情况,请使用dis*查看生成的指令。对于第一个函数,包含分配:

from dis import dis
dis(func)
  2           0 LOAD_CONST               1 (42)
              2 STORE_FAST               0 (a)

  3           4 LOAD_FAST                0 (a)
              6 RETURN_VALUE

而对于第二个功能:

dis(func2)
  2           0 LOAD_CONST               1 (42)
              2 RETURN_VALUE

第一个中使用了另外两个(快速)指令:STORE_FASTLOAD_FAST。这些可以快速存储并获取fastlocals当前执行帧数组中的值。然后,在两种情况下RETURN_VALUE都执行a。所以,第二个是曾经如此轻微,由于执行需要更少的命令更快。

通常,请注意CPython编译器在执行的优化中是保守的。它并没有像其他编译器一样聪明(通常也有更多的信息可以使用)。除了显然是正确的以外,主要设计目标是:a)保持简单,并且b)尽可能快地进行编译,因此您甚至不会注意到存在编译阶段。

最后,您不应该为像这样的小问题而烦恼。速度的好处是微小的,恒定的,并且与解释Python事实所带来的开销相形见war。

* dis是一个小的Python模块,可反汇编您的代码,您可以使用它查看VM将执行的Python字节码。

注意:正如@Jorn Vernee的评论中所述,这特定于Python的CPython实现。如果其他实现愿意的话,其他实现可能会进行更积极的优化,而CPython则不需要。


11
不是python人(c ++),所以我不知道它是如何工作的,但是第一种情况不应该针对第二种情况进行优化吗?一个不错的C ++编译器将进行优化。
NathanOliver

7
@NathanOliver确实不是,Python会按照此处的指示进行操作,甚至不会尝试巧妙地发挥它的作用。
Dimitris Fasarakis Hilliard

80
在我看来,@ NathanOliver 对这个问题的答案的完全合理和明智的猜测是完全错误的事实,这证明这不是可以回答的“不言自明”,“胡说八道”,“愚蠢”问题。通过“花点时间思考”,就像TigerhawkT3会让我们相信的那样。尽管有多年专业的Python程序员的经验,但我不确定答案是一个有效,有趣的问题。
Mark Amery

Python的编译器充其量是“保守的”,而不是“非常保守的”。主要设计目标不是“尽可能快……因此您甚至不会注意到存在编译阶段”。在“保持简单”之后,这是次要的。具有大常量的函数,例如“ 1 <<(2 ** 34)”和“ b'x'*(2 ** 32)”,需要花费几秒钟的时间来编译并生成GB大小的常量,即使该函数从不跑。大字符串甚至会被编译器丢弃。针对这些情况的建议修复已被拒绝,因为它们会使编译器过于复杂。
安德鲁·达尔克

@AndrewDalke感谢内部人士对此的评论,我对措辞进行了调整,以解决您指出的问题。
Dimitris Fasarakis Hilliard's

3

两者基本上是相同的,除了在第一种情况下,对象42只是简单地分配给名为的变量a,换句话说,名称(即a)是指值(即42)。从某种意义上说,它从不复制任何数据,从技术上讲它不会做任何分配。

returning期间,此命名绑定a在第一种情况下返回,而对象42在第二种情况下返回。

有关更多阅读,请参考Ned Batchelder的精彩文章

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.