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

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

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

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

情况1

def func():
    a = 42
    return a

情况2

def func():
    return 42

Is there any ultimate difference between the following two code snippets? The first assigns a value to a variable in a function and then returns that variable. The second function just returns the value directly.

Does Python turn them into equivalent bytecode? Is one of them faster?

Case 1:

def func():
    a = 42
    return a

Case 2:

def func():
    return 42

回答 0

不,不是

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则不需要。

No, it doesn’t.

The compilation to CPython byte code is only passed through a small peephole optimizer that is designed to do only basic optimizations (See test_peepholer.py in the test suite for more on these optimizations).

To take a look at what’s actually going to happen, use dis* to see the instructions generated. For the first function, containing the assignment:

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

While, for the second function:

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

Two more (fast) instructions are used in the first: STORE_FAST and LOAD_FAST. These make a quick store and grab of the value in the fastlocals array of the current execution frame. Then, in both cases, a RETURN_VALUE is performed. So, the second is ever so slightly faster due to less commands needed to execute.

In general, be aware that the CPython compiler is conservative in the optimizations it performs. It isn’t and doesn’t try to be as smart as other compilers (which, in general, also have much more information to work with). The main design goal, apart from obviously being correct, is to a) keep it simple and b) be as swift as possible in compiling these so you don’t even notice that a compilation phase exists.

In the end, you shouldn’t trouble yourself with small issues like this one. The benefit in speed is tiny, constant and, dwarfed by the overhead introduced by the fact that Python is interpreted.

*dis is a little Python module that dis-assembles your code, you can use it to see the Python bytecode that the VM will execute.

Note: As also stated in a comment by @Jorn Vernee, this is specific to the CPython implementation of Python. Other implementations might do more aggressive optimizations if they so desire, CPython doesn’t.


回答 1

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

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

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

Both are basically the same except that in the first case the object 42 is simply aassigned to a variable named a or, in other words, names (i.e. a) refer to values (i.e. 42) . It doesn’t do any assignment technically, in the sense that it never copies any data.

While returning, this named binding a is returned in the first case while the object 42 is return in the second case.

For more reading, refer this great article by Ned Batchelder