python 试图理解调用堆栈

python trying to understand callstacks

我正在尝试理解下面代码的调用堆栈,但我无法回答。

为什么这一行 return 4:

7. return z

为什么是第 5 行:

16 x = x + m1(x) 

希望你能帮助我。

1  def m1(x): 
2      x = x + 1
3      z = m2(x, 4)
4      x = x + 1
5      z = z + 1
6      print(x)
7      return z
8 
9  def m2(x, y):
10     x = x + 1
11     y = y + 1
12     print(x, y)
13     return x
14
15 x = 1
16 x = x + m1(x)

这是因为变量 x 和 z 是不可变的。
在 python 中,当我们将不可变参数传递给函数时,通过引用调用不再适用。
一旦我们更改此类变量的值,函数就会创建自己的副本,即局部变量。这就是背后的原因。
Please refer this for more information

我想你需要知道的是函数内部的变量与函数外部的变量是不同的。或者更准确地说,每次调用函数时,都会为该函数调用创建一组新变量,与可能碰巧具有相同名称的其他任何变量无关。 (除了极少数例外。)所以,例如,如果你写

def f1(x): 
    x = x + 1
    return x

x = 1
x = f1(x)
x = f1(x)
print(x)

实际上有三个单独的变量名为 x:有一个在顶层 ("global"),它被初始化为 1,为第一次调用 [=18 创建的=],以及为第二次调用 f1 创建的那个。如果您对它们的名称进行注释以将它们彼此区分开来,实际上会发生以下情况:

x_global = 1

# Python does this behind the scenes
f1_call1_argument1 = x_global
# call f1
x_f1_call1 = f1_call1_argument1
# now your code in f1 starts running
x_f1_call1 = x_f1_call1 + 1
f1_call1_return_value = x_f1_call1
# return from f1
x_global = f1_call1_return_value

# now repeat but with the second call to f1
f1_call2_argument1 = x_global
x_f1_call2 = f1_call2_argument1
x_f1_call2 = x_f1_call2 + 1
f1_call2_return_value = x_f1_call2
x_global = f1_call2_return_value

print(x_global)

从这里您可以看到不同的变量是如何分开的,即使它们在代码中具有相同的名称。每个变量都有自己的 "area" 种类,其中名称指的是该变量,而在不同的 "area" 中,相同的名称指的是不同的变量。这叫做scoping.


我提到过有一些例外。这里有两个比较常见的:

  • Python 允许您在函数内部放置一个 "global declaration" 当您希望该名称在函数内部和函数外部引用相同的事物时。因此,如果我这样定义 f2

    def f2():
        global x
        x = x + 1
        return x
    

    那么就不会有 x 的特定功能 ("local") 版本。它只会使用全局 x。 运行 这个代码

    x = 1
    x = f2()
    x = f2()
    

    会像这样工作:

    x_global = 1
    
    # call f2
    x_global = x_global + 1
    f2_call1_return_value = x_global
    # return from f2
    x_global = f2_call1_return_value
    
    x_global = x_global + 1
    f2_call2_return_value = x_f1_call2
    x_global = f2_call2_return_value
    
  • 您还可以为参数设置默认值,并且由于 Python 实现方式中的设计决策,默认值有效地存储在持续存在的不可见变量中函数调用之间。如果我有

    def f3(x=[]):
        x.append(5)
        return x
    
    x = [1]
    x = f3()
    x = f3()
    

    (我使用的是一个列表,因为它可以改变,但一个整数不能)那么它是这样工作的:

    # as part of the definition of f3
    f3_argument1_default = []
    
    x_global = [1]
    
    # first call to f3
    f3_call1_argument1 = f3_argument1_default
    x_f3_call1 = f3_call1_argument1
    x_f3_call1.append(5)                      # f3_argument1_default is now  [1,5]
    f3_call1_return_value = x_f3_call1
    x_global = f3_call1_return_value
    
    # second call to f3
    f3_call2_argument1 = f3_argument1_default
    x_f3_call2 = f3_call2_argument1
    x_f3_call2.append(5)                      # f3_argument1_default is now  [1,5,5]
    f3_call2_return_value = x_f3_call2
    x_global = f3_call2_return_value