【问题标题】:Why doesn't Python optimize away temporary variables?为什么 Python 不优化掉临时变量?
【发布时间】:2015-03-11 17:37:17
【问题描述】:

Fowler 的 Extract Variable 重构方法,以前的 Introduce Explaining Variable,说使用临时变量使代码对人类更清晰。这个想法是通过引入一个不需要的局部变量来阐明复杂的代码,并为说明目的命名该变量。它还提倡这种对 cme​​ts 的解释。Other languages 优化掉临时变量,因此没有时间或空间资源成本。为什么 Python 不这样做?

在 [3] 中:def multiple_of_six_fat(n): ...:multiple_of_two = n%2 == 0 ...:multiple_of_three = n%3 == 0 ...:返回 multiple_of_two 和 multiple_of_three ...: 在 [4] 中:def multiple_of_six_lean(n): ...:返回 n%2 == 0 和 n%3 == 0 ...: 在[5]中:导入dis [6] 中:dis.dis(multiple_of_six_fat) 2 0 加载快速 0 (n) 3 负载常数 1 (2) 6 BINARY_MODULO 7 负载常数 2 (0) 10 COMPARE_OP 2 (==) 13 STORE_FAST 1 (multiple_of_two) 3 16 加载快速 0 (n) 19 负载常数 3 (3) 22 BINARY_MODULO 23 负载常数 2 (0) 26 比较_OP 2 (==) 29 STORE_FAST 2 (multiple_of_three) 4 32 LOAD_FAST 1 (multiple_of_two) 35 跳转_IF_FALSE_OR_POP 41 38 LOAD_FAST 2 (multiple_of_three) >> 41 返回值 在 [7] 中:dis.dis(multiple_of_six_lean) 2 0 加载快速 0 (n) 3 负载常数 1 (2) 6 BINARY_MODULO 7 负载常数 2 (0) 10 COMPARE_OP 2 (==) 13 跳转_IF_FALSE_OR_POP 29 16 加载快速 0 (n) 19 负载常数 3 (3) 22 BINARY_MODULO 23 负载常数 2 (0) 26 比较_OP 2 (==) >> 29 返回值

【问题讨论】:

    标签: python optimization refactoring python-internals


    【解决方案1】:

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

    比较以下,例如:

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

    如果 Python “优化”了 ob1ob2 变量,行为就会改变。

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

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

    【讨论】:

    • 不错的简单示例,但想知道是否有一个看起来更像功能而不是错误的示例。说这些应该产生不同的结果会破坏你的论点。
    • @BobStein-VisiBone:一个示例的行为说明了绑定的重要性。还有更多,但它们需要更多时间来构建和解释。
    • 您使用的论点是“Python 并不意味着要快”。但是该语言的最终成功取决于它的实际应用程序是否快速,以及它的源代码是否易于理解。我试图指出这些权衡的一种(当前)情况。您通过解释为什么编译器很难回答我的问题。但是你几乎是在暗示这是不可能和不可取的。
    • 顺便说一句,为什么在同一个表达式中实例化的两个对象应该是相同的?为什么第一个例子应该是真的??
    • @BobStein-VisiBone:对象相同。相反,对象 id 值被重用;该值仅保证在对象的生命周期内是唯一的。因为在第一个例子中取出对象id后没有对对象的引用,所以对象被清空,id可以被第二个对象重用。
    【解决方案2】:

    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)
      
    • 一位核心开发人员表示,“它不太可能在实际代码中提供任何加速,我认为我们不应该增加编译器的复杂性。”

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-20
      • 1970-01-01
      • 2015-03-22
      • 1970-01-01
      • 2014-05-09
      • 2019-07-06
      • 2015-07-28
      相关资源
      最近更新 更多