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

时间:2021-08-18 20:46:09

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?

Python会将它们转换为等效的字节码吗?其中一个更快吗?

Case 1:

情况1:

def func():
    a = 42
    return a

Case 2:

案例2:

def func():
    return 42

2 个解决方案

#1


134  

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).

CPython字节代码的编译仅通过一个小的窥孔优化器传递,该优化器仅用于进行基本优化(有关这些优化的更多信息,请参阅测试套件中的test_peepholer.py)。

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:

要查看实际发生的情况,请使用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

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.

在第一个中使用了两个(快速)指令:STORE_FAST和LOAD_FAST。这些可以快速存储并获取当前执行帧的fastlocals数组中的值。然后,在这两种情况下,执行RETURN_VALUE。因此,由于执行所需的命令较少,因此第二种情况会稍微快一些。

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.

通常,请注意CPython编译器在其执行的优化中是保守的。它不是也不会试图像其他编译器那样聪明(通常,它们也可以使用更多的信息)。除了明显正确之外,主要的设计目标是:a)保持简单,b)尽可能快地编译这些目标,这样你甚至不会注意到存在编译阶段。

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.

最后,你不应该为像这样的小问题困扰自己。速度的好处是微小的,恒定的,并且由Python解释的事实引入的开销相形见绌。

*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.

* dis是一个用于解组代码的Python模块,您可以使用它来查看VM将执行的Python字节码。

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.

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

#2


3  

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.

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

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

在返回时,在第一种情况下返回该命名绑定a,而在第二种情况下返回该对象42。

For more reading, refer this great article by Ned Batchelder

如需更多阅读,请参阅Ned Batchelder撰写的这篇精彩文章

#1


134  

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).

CPython字节代码的编译仅通过一个小的窥孔优化器传递,该优化器仅用于进行基本优化(有关这些优化的更多信息,请参阅测试套件中的test_peepholer.py)。

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:

要查看实际发生的情况,请使用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

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.

在第一个中使用了两个(快速)指令:STORE_FAST和LOAD_FAST。这些可以快速存储并获取当前执行帧的fastlocals数组中的值。然后,在这两种情况下,执行RETURN_VALUE。因此,由于执行所需的命令较少,因此第二种情况会稍微快一些。

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.

通常,请注意CPython编译器在其执行的优化中是保守的。它不是也不会试图像其他编译器那样聪明(通常,它们也可以使用更多的信息)。除了明显正确之外,主要的设计目标是:a)保持简单,b)尽可能快地编译这些目标,这样你甚至不会注意到存在编译阶段。

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.

最后,你不应该为像这样的小问题困扰自己。速度的好处是微小的,恒定的,并且由Python解释的事实引入的开销相形见绌。

*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.

* dis是一个用于解组代码的Python模块,您可以使用它来查看VM将执行的Python字节码。

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.

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

#2


3  

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.

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

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

在返回时,在第一种情况下返回该命名绑定a,而在第二种情况下返回该对象42。

For more reading, refer this great article by Ned Batchelder

如需更多阅读,请参阅Ned Batchelder撰写的这篇精彩文章