def outter():def innerA():x = 100def innerB():nonlocal xx = 250def innerC():global xx = 520x = 880innerA()print(f"调用完 innerA() 函数之后,x = {x}")innerB()print(f"调用完 innerB() 函数之后,x = {x}")innerC()print(f"调用完 innerC() 函数之后,x = {x}")outter()print(f"此时此刻,全局变量 x = {x}")
调用完 innerA() 函数之后,x = 880调用完 innerB() 函数之后,x = 250调用完 innerC() 函数之后,x = 250此时此刻,全局变量 x = 520
在这个代码中,我们通过嵌套函数以及不同作用域(LEGB:Local、Enclosing、Global、Built-in)来理解变量 x 的变化。接下来,我会逐行分析,并结合 Python 中 nonlocal 和 global 的规则来解释每一行代码的执行结果。
def outter(): def innerA(): x = 100 def innerB(): nonlocal x x = 250 def innerC(): global x x = 520 x = 880 innerA() print(f"调用完 innerA() 函数之后,x = {x}") innerB() print(f"调用完 innerB() 函数之后,x = {x}") innerC() print(f"调用完 innerC() 函数之后,x = {x}") outter() print(f"此时此刻,全局变量 x = {x}")
逐行分析:
def outter():
定义了一个名为 outter 的外部函数。
def innerA():
定义了一个名为 innerA 的内部函数。
x = 100
在 innerA() 中声明了一个局部变量 x = 100,但这个 x 仅在 innerA() 函数内部存在。
def innerB():
定义了另一个名为 innerB 的内部函数。
nonlocal x
这里使用 nonlocal 关键字,表示要修改的是 outter() 函数作用域中的 x,而不是 innerB() 内部的局部变量。如果 nonlocal 没有找到外部作用域的变量,它会抛出 SyntaxError。
x = 250
在 innerB() 中修改了 outter() 函数作用域中的 x 为 250。
def innerC():
定义了名为 innerC 的内部函数。
global x
这里使用 global 关键字,表示要修改的是全局作用域中的 x。这意味着 innerC() 中对 x 的修改将会影响全局变量 x。
x = 520
在 innerC() 中将全局变量 x 的值改为 520。
x = 880
在 outter() 中定义了一个局部变量 x = 880,这是 outter() 的作用域内的 x,它与 innerA()、innerB() 和 innerC() 函数的 x 是不同的。
innerA()
调用 innerA(),但由于 innerA() 只改变了 innerA() 内部的 x,而没有影响到 outter() 中的 x(因为 x = 100 是局部变量),所以 outter() 中的 x 依然保持为 880。
输出结果是:调用完 innerA() 函数之后,x = 880
innerB()
调用 innerB()。在 innerB() 中,nonlocal x 会改变 outter() 中的 x 为 250。因此,outter() 中的 x 变成了 250。
输出结果是:调用完 innerB() 函数之后,x = 250
innerC()
调用 innerC()。在 innerC() 中,global x 会改变全局变量 x 的值为 520。因此,x 在全局作用域中的值变成了 520。
输出结果是:调用完 innerC() 函数之后,x = 250 这里的 x = 250 是 outter() 作用域中的 x,而不是全局变量。
outter()
调用 outter() 函数结束后,函数内部的所有局部变量和作用域都将被销毁。但全局变量 x 已经被 innerC() 修改为 520。
print(f"此时此刻,全局变量 x = {x}")
输出全局变量 x 的值,此时全局变量 x 已经被 innerC() 修改为 520。
输出结果是:此时此刻,全局变量 x = 520
输出结果:
调用完 innerA() 函数之后,x = 880 调用完 innerB() 函数之后,x = 250 调用完 innerC() 函数之后,x = 250 此时此刻,全局变量 x = 520
解释:
LEGB规则:
L(Local):局部作用域。x = 100 在 innerA 内部作用域,只在 innerA 内部有效。
E(Enclosing):外层作用域。nonlocal 会改变外层作用域(即 outter())中的 x。
G(Global):全局作用域。global 会影响全局作用域中的 x。
nonlocal:
用来引用最近的外层函数(非全局)作用域中的变量,修改它的值。
在 innerB 中,nonlocal x 修改了 outter() 作用域中的 x。
global:
用来引用全局作用域中的变量,修改它的值。
在 innerC 中,global x 修改了全局作用域中的 x。
在 Python 中,`LEGB` 规则用于确定变量的查找顺序。具体来说,`LEGB` 代表:
- **L** (Local):当前函数或代码块内的局部变量。
- **E** (Enclosing):外层函数的局部变量(即封闭作用域)。
- **G** (Global):模块级别的全局变量。
- **B** (Built-in):Python 预定义的内建变量和函数。
当 Python 执行变量查找时,它会按照 **LEGB** 的顺序查找,直到找到该变量为止。如果一个变量在某个作用域中没有定义,Python 会继续向外层作用域查找,直到最外层的内建作用域。
### `global` 和 `nonlocal` 的作用
- **`global`**:用来声明某个变量是在 **全局作用域**(模块级别)中,而非当前函数的局部变量。如果你在一个函数中使用 `global` 声明一个变量,Python 会把这个变量视为全局变量,从而可以修改全局作用域中的该变量。
- **`nonlocal`**:用来声明某个变量是在 **封闭作用域**(即外层函数的局部作用域)中,而非当前函数的局部变量。如果你在嵌套函数中使用 `nonlocal`,Python 会把这个变量视为外层函数(而不是全局作用域)中的变量,并且可以修改它。
### `global` 和 `nonlocal` 会冲突吗?
**会冲突,但只有在它们操作同一个变量时才会有问题。**
- **`global`** 和 **`nonlocal`** 不会直接冲突,但是它们的使用方式和适用范围是不同的。
- `global` 会操作全局作用域中的变量。
- `nonlocal` 会操作外层(非全局)作用域中的变量。
如果你在同一个函数中同时使用 `global` 和 `nonlocal` 来声明同一个变量,则会产生 **冲突**,因为它们作用于不同的作用域(`global` 修改全局变量,而 `nonlocal` 修改外层作用域中的变量),这可能导致意外的行为或错误。
### 示例:`global` 和 `nonlocal` 的冲突
```python
x = 10 # 全局变量
def outer():
x = 20 # outer 函数中的局部变量
def inner():
nonlocal x # 尝试修改 outer 中的 x
global x # 尝试修改全局变量 x
x = 30 # 这将修改 outer 中的 x
print(f"inner 函数中的 x = {x}")
inner()
print(f"outer 函数中的 x = {x}")
outer()
print(f"全局变量 x = {x}")
```
### 分析:
1. 在 `outer()` 函数内,`x = 20` 是 `outer` 局部作用域中的变量。
2. `inner()` 中使用了 `nonlocal x` 来修改 `outer()` 中的 `x`,同时又使用 `global x` 来修改全局变量 `x`。
3. `nonlocal x` 会修改 `outer()` 中的 `x`,而 `global x` 会修改全局变量 `x`。
### 输出:
```python
inner 函数中的 x = 30
outer 函数中的 x = 30
全局变量 x = 30
```
### 解释:
- 在 `inner()` 中,`nonlocal x` 让 Python 修改 `outer()` 中的 `x`,将其变为 30。
- `global x` 将全局变量 `x` 的值也修改为 30。
- 最终,`outer()` 中的 `x` 和全局变量 `x` 都变成了 30。
### 是否会引发错误?
在上述代码中,虽然 `global` 和 `nonlocal` 分别操作了不同作用域中的 `x`,所以它们并不会直接冲突。不过,如果你试图同时在同一个作用域中使用 `global` 和 `nonlocal` 来声明相同的变量,就会引发 **`SyntaxError`**。
例如:
```python
def foo():
nonlocal x
global x # 这将导致语法错误
```
### 总结:
- **`global` 和 `nonlocal` 不会直接冲突**,但它们作用于不同的作用域。
- 如果同时在同一个函数中尝试使用 `global` 和 `nonlocal` 来修改同一个变量,它们会分别修改不同作用域中的变量,可能会导致混乱和错误。
- Python 不允许在同一个作用域内同时使用 `global` 和 `nonlocal` 来声明同一个变量,这会导致 `SyntaxError`。