为什么 Python 不优化掉临时变量?

Why doesn't Python optimize away temporary variables?

Fowler 的 Extract Variable refactoring method, formerly Introduce Explaining Variable, says use a temporary variable to make code clearer for humans. The idea is to elucidate complex code by introducing an otherwise unneeded local variable, and naming that variable for exposition purposes. It also advocates this kind of explaining over comments.. Other languages 优化了临时变量,因此没有时间或 space 资源成本。为什么 Python 不这样做?

在 [3] 中:def multiple_of_six_fat(n):
   ...: multiple_of_two = n%2 == 0
   ...: multiple_of_three = n%3 == 0
   ...: return multiple_of_two 和 multiple_of_three
   ...:

在 [4] 中:def multiple_of_six_lean(n):
   ...: return n%2 == 0 和 n%3 == 0
   ...:
在 [5] 中:导入磁盘

在 [6] 中:dis.dis(multiple_of_six_fat)
  2 0 LOAD_FAST 0 (n)
              3 LOAD_CONST 1 (2)
              6 BINARY_MODULO
              7 LOAD_CONST 2 (0)
             10 COMPARE_OP 2 (==)
             13 STORE_FAST 1 (multiple_of_two)

  3 16 LOAD_FAST 0 (n)
             19 LOAD_CONST 3 (3)
             22 BINARY_MODULO
             23 LOAD_CONST 2 (0)
             26 COMPARE_OP 2 (==)
             29 STORE_FAST 2 (multiple_of_three)

  4 32 LOAD_FAST 1 (multiple_of_two)
             35 JUMP_IF_FALSE_OR_POP 41
             38 LOAD_FAST 2 (multiple_of_three)
        >> 41 RETURN_VALUE

在 [7] 中:dis.dis(multiple_of_six_lean)
  2 0 LOAD_FAST 0 (n)
              3 LOAD_CONST 1 (2)
              6 BINARY_MODULO
              7 LOAD_CONST 2 (0)
             10 COMPARE_OP 2 (==)
             13 JUMP_IF_FALSE_OR_POP 29
             16 LOAD_FAST 0 (n)
             19 LOAD_CONST 3 (3)
             22 BINARY_MODULO
             23 LOAD_CONST 2 (0)
             26 COMPARE_OP 2 (==)
        >> 29 RETURN_VALUE

因为 Python 是一种高度动态的语言,引用会影响行为。

比较以下,例如:

>>> id(object()) == id(object())
True
>>> ob1 = object()
>>> ob2 = object()
>>> id(ob1) == id(ob2)
False

如果 Python 'optimised' ob1ob2 变量消失,行为就会改变。

Python 对象生命周期由引用计数决定。将 weak references 添加到混合线程中,您会发现优化变量(甚至是本地变量)会导致不良的行为变化。

此外,在 Python 中,从性能角度来看,删除这些变量几乎不会改变任何东西。本地命名空间已经高度优化(值通过数组中的索引查找);如果您担心取消引用局部变量的速度,那么您在项目的那个时间关键部分使用了错误的编程语言。

Issue 2181(在函数末尾优化局部变量)有一些有趣的点:

  • 它可以使调试更加困难,因为符号不再 存在。 Guido says only do it for -O.
  • 可能会破坏 inspectsys._getframe() 的一些用法。
  • 更改对象的生命周期。例如下例中的 myfunc 优化后可能会失败,因为此时 Python 保证在函数退出之前文件对象没有关闭。 (糟糕的风格,但仍然)

    def myfunc():
        f = open('somewhere', 'r')
        fd = f.fileno()
        return os.fstat(fd)
    

    不能改写为:

    def bogus():
        fd = open('somewhere', 'r').fileno()
        # the file is auto-closed here and fd becomes invalid
        return os.fstat(fd)
    
  • 一位核心开发人员说 "it is unlikely to give any speedup in real-world code, I don't think we should add complexity to the compiler."