环境链条(Environment Chain)

for example:
屏幕截图 2024-12-17 114451



环境中的绑定和求值(Binding and Evaluation)

1. 变量赋值(Assigning)


2. 函数定义(Def statements)


3. 函数调用(Calling a Function)


4. 局部环境与父环境


总结

  1. 赋值:变量被绑定到当前环境的数值。
  2. def语句:函数定义时绑定名称,并记录定义时的环境。
  3. 函数调用:创建局部环境,将参数绑定到实参,并连接到父环境。

这种机制确保了程序可以正确地查找变量和参数值,形成一个清晰的环境链条。



局部环境

函数调用时会创建新的局部环境,这是为了确保函数执行时的数据独立性局部作用域,从而实现函数的正确行为和可重复使用。下面详细解释为什么会创建局部环境:

1. 数据独立性(隔离变量)

函数的局部环境可以保证局部变量与外部变量隔离,不会相互影响。

示例

def foo(x):
    y = x + 2
    return y

a = foo(3)  # 调用时,x = 3,y 只在这个调用中存在
b = foo(5)  # 调用时,x = 5,这是新的局部环境

原因:如果不创建局部环境,那么同名变量会在多个调用中互相覆盖,导致错误结果。


2. 函数的参数绑定

函数调用时,参数(形式参数)需要与传入的值(实际参数)进行绑定。这一绑定过程发生在新的局部环境中。

示例

def add(a, b):
    return a + b

add(2, 3)  # 局部环境绑定:a = 2, b = 3
add(5, 7)  # 局部环境绑定:a = 5, b = 7

3. 递归调用的需要

如果没有新的局部环境,递归调用无法实现,因为每一层递归调用都需要独立的参数和中间状态。

示例

def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)

factorial(3)

原因:如果没有独立的局部环境,不同递归层次的 n 就会互相覆盖,导致无法正确求值。


4. 函数的可复用性

创建新的局部环境可以确保函数可以在不同上下文中调用,并返回正确的结果。

示例

def square(x):
    return x * x

a = square(2)  # 创建局部环境,x = 2
b = square(4)  # 创建新的局部环境,x = 4

5. 总结:为什么会创建新的局部环境?

函数调用时创建新的局部环境的根本原因是:

  1. 隔离局部变量,保证数据独立,不影响其他函数调用。
  2. 参数绑定,将实际参数传递给形式参数。
  3. 支持递归,为每一层调用保存独立的状态。
  4. 提高可复用性,使函数能够独立运行。

局部环境使函数调用成为一个封闭的计算单元,有效管理变量作用域,保证程序的正确性和可维护性。



return f 和 return f(x) 的区别

return f 返回的是 函数对象 f。
return f(x) 返回的是 函数 f 执行后的结果,即 f(x) 的计算值。

报错举例:
你遇到的错误是:

NameError: name 'x' is not defined

这是因为在函数 incr(n) 中,你尝试直接调用 f(x),但是在此时 x 并没有定义。这导致 Python 无法找到 x 的值,产生了 NameError 错误。

具体原因:

def incr(n):
    def f(x):
        return n + x
    return f(x)  # 错误:x 在这里没有定义

在这个代码中:

正确的做法:

如果你的目的是返回 f 函数本身(即将 f 作为闭包返回),而不是立即执行它,你应该改为 return f,而不是 return f(x)。然后,稍后你可以使用参数来调用 f

修改后的代码:

def incr(n):
    def f(x):
        return n + x
    return f  # 返回函数 f 本身,而不是调用 f(x)

result_function = incr(5)  # 返回 f 函数
result = result_function(6)  # 调用 f(6)
print(result)  # 输出 5 + 6 = 11

解释:

  1. incr(5) 返回的是 f 函数,而不是 f(x) 的执行结果。
  2. 返回的 f 函数被赋值给 result_function,然后我们调用 result_function(6),即实际调用的是 f(6)
  3. f(6) 会计算 n + x,其中 n 是从 incr(5) 中传递来的 5,所以 f(6) 返回的是 5 + 6 = 11,最终打印结果是 11

关键修改:

这样就能正确地实现闭包的功能。



控制语句 Control

条件表达式 三元表达式 A if B else C :
image

关于True Values

false value 包括:
False
None
0
Empty strings, sets, lists, tuples, and dictionaries
其他全是true value


A if B else C 举例代码分析

这段代码包含了两个条件表达式(Python 的三元运算符 A if B else C),整体是一个比较表达式:

13 if 0 else 5 == 13 if [] else 5
  1. 左侧部分:13 if 0 else 5

    • 条件 0 是布尔值 False
    • 因此,选择 else 后的值 5

    结果:左侧为 5


  1. 右侧部分:13 if [] else 5

    • 条件 [] 是空列表,在布尔上下文中,它也是 False
    • 因此,选择 else 后的值 5

    结果:右侧为 5


  1. 比较运算:5 == 5
    • 左侧和右侧的结果都是 5,所以 5 == 5True

最终结果

输出 True



  1. short-circuit evaluation
    left and right 和 left or right 的短路特性
    image

left and right:先计算left,如果left为false,表达式的值为left,不计算right的值(不判断right是否有意义,如1/0);否则,表达式值为right。
left or right: 先计算left,如果left为true,表达式的值为left,不计算right的值(不判断right是否有意义,如1/0);否则,表达式值为right。