【问题标题】:Variable Reassignment in Python-- ontological query -- using Newton MethodPython中的变量重新赋值——本体查询——使用牛顿法
【发布时间】:2018-03-17 17:01:18
【问题描述】:

我正在阅读 Miller 和 Ranum 的关于使用 Python 的算法和数据结构的书。他们使用以下示例:

def squareroot(n):
    root = n/2
    for k in range(20):
        root = (1/2)*(root + n / root)

    return root 

我的问题是,变量 'root' 在 for 循环中被重新分配,因此每次迭代时,赋值运算符右侧的表达式中的 'root' 的值都会发生变化。我不确定我是否理解这怎么可能。

一旦进行函数调用,for 循环外的“root”变量(在第 2 行)的计算结果为一个值,然后由 for 循环块表达式中的“root”变量引用,允许表达式计算为单个值,该值在for循环块中重新分配给赋值运算符左侧的变量“root”。在下一次迭代开始时,'root' 不再是 n/2,而是 for 循环中的表达式计算的任何值。在这种情况下,变量“root”被重新分配了一个浮点值,因此不再是它最初定义的——一个使用“root”变量的表达式。

例如,如果我们使用函数调用 squareroot(9),'root' 在第一次迭代后将保持 3.25 的值,因为 for 循环中的表达式计算为该值。一旦在 for 循环中为变量 'root' 重新分配了一个浮点值,最初定义为 'root' 的表达式就会被破坏。 'root' 已被重新定义为 3.25。在 for 循环中,'root' 不再指代表达式,而是指单个浮点值。然而,在这个例子中,for 循环中的“root”变量似乎在每次迭代之后都有两个含义:它既是浮点值又是表达式。我不明白这是怎么回事。

【问题讨论】:

  • 如你所见,变量“作为浮点值”和“作为表达式”有什么区别?
  • 变量从不“引用表达式”。它们指的是评估该表达式的。因此,例如,在执行root = n/2 的那一刻,root 持有一个特定的数值,该数值恰好是当时n 持有的数值的一半;与 n 的值没有持续的联系。

标签: python-3.x function for-loop newtons-method


【解决方案1】:

简短的回答是 Python 不会将表达式视为抽象公式;它将其视为要执行的一系列具体计算。每次循环时,它都会使用root 的当前值执行这些计算,然后使用结果更新root 的值。查看 Python 执行的实际操作序列可能会有所帮助,如下所示:

import dis
def squareroot(n):
    root = n/2
    for k in range(20):
        root = 0.5 * (root + n / root)
    return root
dis.dis(squareroot)

结果:

  2           0 LOAD_FAST                0 (n)
              3 LOAD_CONST               1 (2)
              6 BINARY_DIVIDE       
              7 STORE_FAST               1 (root)

  3          10 SETUP_LOOP              38 (to 51)
             13 LOAD_GLOBAL              0 (range)
             16 LOAD_CONST               2 (20)
             19 CALL_FUNCTION            1
             22 GET_ITER            
        >>   23 FOR_ITER                24 (to 50)
             26 STORE_FAST               2 (k)

  4          29 LOAD_CONST               3 (0.5)
             32 LOAD_FAST                1 (root)
             35 LOAD_FAST                0 (n)
             38 LOAD_FAST                1 (root)
             41 BINARY_DIVIDE       
             42 BINARY_ADD          
             43 BINARY_MULTIPLY     
             44 STORE_FAST               1 (root)
             47 JUMP_ABSOLUTE           23
        >>   50 POP_BLOCK           

  5     >>   51 LOAD_FAST                1 (root)
             54 RETURN_VALUE        

有趣的部分是以4 开头的块,它对应于您询问的作业。这就是发生的事情:

  • 将以下内容加载到堆栈中:[0.5,root 的当前内容,n 的当前内容,root 的当前内容]。在第一次迭代期间(n=9),堆栈现在将保存 [0.5, 4.5, 9.0, 4.5]。
  • 将堆栈中倒数第二项除以最后一项并将结果放入堆栈:堆栈现在为 [0.5, 4.5, 2.0]
  • 将最后两项添加到堆栈中并将结果放入堆栈中:堆栈现在为 [0.5, 6.5]
  • 将堆栈中的最后两项相乘,然后将结果放入堆栈:堆栈现在为 [3.25]
  • 将堆栈中的最后一项 (3.25) 存储在变量 root 中。

因此,如您所见,表达式仅表示要遵循的一系列步骤。在这些步骤结束时,结果将存储到root。然后可以使用root 的新值再次执行这些步骤。

【讨论】:

  • 这很棒。谢谢你这样做——比我给出的插图要好得多。不过,我仍在努力理解。 'root' 的新值在第一次迭代后是 3.25,因此没有表达式可以在每次后续迭代中放入该值,因为您建议“可以使用 root 的新值再次执行这些步骤”,但它是我认为 root 的新值是 3.25 而不是 (1/2) * (root 的新值 + (n / root 的新值))。换句话说,表达式不再存在 - 'root' 的值已被重新分配。
  • @efw,听起来您可能来自函数式编程背景,但 Python 更具有程序性。 Python 程序只包含一系列将被执行的命令,例如脚本。在这种情况下,Python 遵循这些语句(如上所述),然后将 k 递增 1,然后重复完全相同的语句。所以下一次,它会做和我描述的完全一样的事情,但是从 root=3.25 而不是 root=4.5 开始。表达式不是分配给根然后丢弃的对象;它是一个脚本,用于计算 root 的新值。
  • @Mathias Fripp 谢谢。我想我理解我的错误。我正在求解变量——我一直在从数学的角度来看待 Python 中的变量,而不是编程语言本身。所以赋值语句中的表达式永远不会消失——它的计算结果是变量引用的值——但表达式仍然作为赋值语句的一部分。变量从不引用表达式,而是引用表达式计算的值。再次感谢您花时间将其置于更大的背景下,以便我能够理解。
  • @efw 是的,Python(和大多数编程语言)并不像您认为的那么聪明!这种特殊的算法利用了root 的旧值将被遗忘并被新值替换的事实。与root[k+1] = 0.5 * (root[k] + n / root[k]) 相同,只是我们不断丢弃旧值,只调用当前值root。即使我们使用那个下标表达式,它也会通过具体的步骤来实现:每次循环,k 都会有一个特定的值,root[k+1] 的值会被一个一个地计算出来。
猜你喜欢
  • 2018-05-19
  • 1970-01-01
  • 2017-11-29
  • 2018-02-26
  • 2018-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-13
相关资源
最近更新 更多