2017-10-05 88 views
1

我正在阅读Miller和Ranum关于使用Python的算法和数据结构的书。他们使用下面的示例:Python中的变量重新分配 - 本体查询 - 使用牛顿方法

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

    return root 

我的问题是,可变“根”被一个for循环中重新分配,使得每次迭代“根”的表达式中的分配的权值操作员更改。我不确定我是否明白这是可能的。

一旦进行函数调用,for循环(第2行)外部的'root'变量计算为一个值,然后由for循环表达式中的'root'变量引用块,允许表达式求值为单个值,该值在for-loop块中重新分配给赋值运算符左侧的变量“root”。在下一次迭代开始时,'root'不再是n/2,但for循环中的表达式已经评估过的任何值。在这种情况下,变量'root'已被重新赋值为float值,因此不再是最初定义的值 - 使用'root'变量的表达式。例如,如果我们使用函数调用squareroot(9),'root'将在第一次迭代后保持3.25的值,因为for-loop中的表达式评估为该值。一旦for循环中的变量'root'被重新分配了一个浮点值,原来定义为'root'的表达式被销毁。 '根'已被重新定义为3.25。 for循环中的'root'不再引用表达式,而是引用单个浮点值。然而,似乎在这个例子中,for循环中的'root'变量在每次迭代之后有两个含义:它既是浮点值又是表达式。我不明白这是怎么回事。

+0

正如你所理解的,变量“作为浮点值”和“作为表达式”有什么区别? – mwchase

+0

变量从不“引用表达式”。它们是指评估该表达式的*值*。因此,例如,执行'root = n/2'的时刻,'root'保存一个特定的数值,恰好是当时'n'的一半;与'n'的值没有持续的联系。 – jasonharper

回答

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]。
  • 将倒数第二个项目与最后一个项目分开,并将结果放在堆栈上:stack is now [0.5,4.5,2.0]
  • 将最后两个项目添加到堆栈并放入栈上的结果是:stack is now [0.5,6.5]
  • 将堆栈中的最后两项相乘,并将结果放在堆栈上:stack is now [3.25]
  • 将最后一项存储在堆栈(3.25)在变量root中。

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

+0

这太好了。谢谢你这样做 - 比我给出的插图要好得多。不过,我仍然试图理解。在第一次迭代之后,'root'的新值为3.25,所以没有表达式将该值放入每个后续迭代中,因为您建议“可以用root的新值再次执行这些步骤”,但它是我认为根的新值是3.25而不是(1/2)*(根的新值+(n /根的新值))。换句话说,表达式不再存在 - 'root'的值已被重新赋值。 – efw

+0

@efw,这听起来像你可能来自函数式编程背景,但Python更具有程序性。 Python程序只包含一系列将被执行的命令,就像脚本一样。在这种情况下,Python遵循这些语句(如上所述),然后将k递增1,然后重复完全相同的语句。所以下一次通过时,它与我描述的完全相同,但是以root = 3.25而不是root = 4.5开头。该表达式不是被分配给root并随后被丢弃的对象;它是一个脚本,用于计算root的新值。 –

+0

@Mathias Fripp谢谢。我想我理解我的错误。我正在为变量求解 - 我一直在通过数学的镜头来看Python中的变量,而不是编程语言本身。所以赋值语句中的表达式永远不会消失 - 它的值是变量引用的值 - 但表达式仍然是赋值语句的一部分。该变量从不引用表达式,而是引用表达式计算的值。再次感谢您抽出宝贵的时间把它放在更大的背景中,以便我能理解。 – efw