Python 嵌套函数间同名变量的作用域规则与 C++/Go/JS 稍有不同,对于读操作而言,Python 的行为与 C++/Go/JS 一致,通过 global/nonlocal 关键字还能做到更灵活,赋值操作则必须要使用 global/nonlocal 关键字才能实现对外层/全局变量的引用。

不过需要注意的是,在函数内部 Python 无法定义同名的局部变量,当然 C++/Go/JS 也无法做到在内层函数直接引用同名的全局变量,这是 Python 与 C++/Go/JS 非常大的不同。

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>

using namespace std;

int x = 0;

void outer() {
x = 1;

[&] { // middle
[&] { // inner
x = 2;
{ // local
int x = 3;
cout << "local:\t" << x << endl;
}
cout << "inner:\t" << x << endl;
}();
cout << "middle:\t" << x << endl;
}();

cout << "outer:\t" << x << endl;
}

int main()
{
outer();
cout << "global:\t" << x << endl;

return 0;
}

输出

1
2
3
4
5
6
7
~$ g++ -o var -std=c++11 var.cc
~$ ./var
local: 3
inner: 2
middle: 2
outer: 2
global: 2

Go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import "fmt"

var x int = 0

func outer() {
x = 1
func() { // middle
func() { // inner
x = 2
{ // local
x := 3
fmt.Println("local:\t", x)
}
fmt.Println("inner:\t", x)
}()

fmt.Println("middle:\t", x)
}()

fmt.Println("outer:\t", x)
}

func main() {
outer()
fmt.Println("global:\t", x)
}

输出

1
2
3
4
5
6
~$ go run var.go
local: 3
inner: 2
middle: 2
outer: 2
global: 2

JS(ES6)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/env node

let x = 0

outer = () => {
x = 1
middle = () => {
inner = () => {
x = 2
{ // local
let x = 3
console.log("local:\t", x)
}
console.log("inner:\t", x)
}

inner()
console.log("middle:\t", x)
}

middle()
console.log("outer:\t", x)
}

outer()
console.log("global:\t", x)

输出

1
2
3
4
5
6
~$ ./var.js
local: 3
inner: 2
middle: 2
outer: 2
global: 2

Python2

Python2 提供了 global 关键字可以在内层函数中显式引用全局变量,如果不使用 global,内层函数的读操作将引用外层函数的变量(如果没有外层函数,则引用全局变量),赋值操作将定义新变量,赋值操作的行为无法做到与 C++/Go/JS 保持一致(global 需要在首次引用同名变量前定义)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#! /usr/bin/env python2

x = 0

def outer():
global x
x = 1
def middle():
def inner():
x = 2
if True:
x = 3
print('local:\t{0}'.format(x))
print('inner:\t{0}'.format(x))

inner()
print('middle:\t{0}'.format(x))

middle()
print('outer:\t{0}'.format(x))

outer()
print('global:\t{0}'.format(x))

输出

1
2
3
4
5
6
~$ ./var2.py 
local: 3
inner: 3
middle: 1
outer: 1
global: 1

Python3

Python3 提供了新的关键字 nonlocal 用于显式引用外层函数的变量(nonlocal 需要在首次引用同名变量前定义),nonlocal 结合 global 使得 Python3 能够做到赋值操作的行为与 C++/Go/JS 保持一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/env python3

x = 0

def outer():
x = 1
def middle():
nonlocal x
def inner():
global x
x = 2
if True:
x = 3
print('local:\t{0}'.format(x))
print('inner:\t{0}'.format(x))

inner()
print('middle:\t{0}'.format(x))

middle()
print('outer:\t{0}'.format(x))

outer()
print('global:\t{0}'.format(x))

输出

1
2
3
4
5
6
~$ ./var3.py 
local: 3
inner: 3
middle: 1
outer: 1
global: 3

注意 nonlocal 不能引用全局变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/env python3

x = 0

def outer():
global x
x = 1
def middle():
nonlocal x
def inner():
global x
x = 2
if True:
x = 3
print('local:\t{0}'.format(x))
print('inner:\t{0}'.format(x))

inner()
print('middle:\t{0}'.format(x))

middle()
print('outer:\t{0}'.format(x))

outer()
print('global:\t{0}'.format(x))

输出

1
2
3
4
5
~$ ./var3.py 
File "./var3.py", line 9
nonlocal x
^
SyntaxError: no binding for nonlocal 'x' found

参考资料

Scope of Variables in Python Tutorial

https://www.datacamp.com/community/tutorials/scope-of-variables-python