Python3基础篇(九)——命名空间和作用域

前言
阅读这篇文章我能学到什么?
  这篇文章将使你弄明白Python3的命名空间和作用域到底意味着什么。

1 命名空间

  命名空间的主要作用是为了防止命名冲突。各个命名空间是独立的,不同命名空间可以具有相同的命名,但同一个命名空间内不得重名(即使变量和函数之间也不能重名)。

1.1 命名空间分类

  Python3有三类命名空间:

  • 内置命名空间:Python3的内置名称(关键字、内置变量名、内置函数名)。
  • 全局命名空间:在函数外及类外定义。
  • 局部命名空间:在函数或类中定义的。

1.2 命名空间的查找顺序

  当访问一个变量或函数时,python根据名称在命名空间中进行查找,但是几种命名空间查找顺序不同。查找顺序优先级为: 局部命名空间>全局命名空间>内置命名空间
  这也就能理解当函数内局部变量和外部
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
Parameter1 = 1
Parameter2 = 2

def abs(): #abs时python的内置函数,位于内置命名空间中,这里将其重载
print("my abs")

def Function():
Parameter1 = 5 #局部命名空间内变量与全局命名空间变量重名
print(Parameter1) #访问到局部命名空间变量
print(Parameter2) #访问到全局命名空间变量

Function()
abs() #全局命名空间与内置命名空间重名,访问到全局命名空间的方法付

运行结果:

1
2
3
5
2
my abs

  函数内变量与全局变量同名时,在函数内部访问的是局部变量,重载内置函数时访问的是重载后的函数。理解了命名空间的优先访问顺序,也就容易理解重载了。

2 作用域

  作用域是Python3程序可以直接访问命名空间的正文区域(作用于是用命名空间的概念来定义的,它强调代码)。类似命名空间,Python3访问一个变量或函数会从内到外依次访问所有的作用域。变量或函数的作用域取决于变量或函数的定义位置,变量或函数作用域确定后,哪些地方具有对其访问的权限也就确定了。

2.1 作用域分类

  Python3的作用域有四种:

  • 局部作用域:为最内层作用域,为函数内部。
  • 嵌套作用域:即非局部也非全局的作用域。当函数(或类)里面又嵌套定义了函数时,对于内层函数来说,外层就是nonlocal。
  • 全局作用域:脚本的最外层。
  • 内建作用域:包含内建的变量、函数、关键字。

2.2 作用域的查找顺序

  查找的顺序优先级为: 内部作用域>嵌套作用域>全局作用域>内建作用域
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Parameter1 = 1                              #全局作用域
#print(Parameter2, Parameter3) #error 不能访问到非局部非全局作用域和局部作用域

def OutFunction():
Parameter2 = 2 #位于非全局非局部作用域
#print(Parameter3) #error 不能访问到局部作用域
def InFunction(): #函数嵌套定义
Parameter3 = 3 #位于局部作用域
print(Parameter2) #可以访问到非局部非全局作用域
print(abs(-1)) #局部作用域访问内建作用域的函数

InFunction() #调用内部函数

OutFunction()

运行结果:

1
2
2
1

  Python3查找变量或函数时按照优先级进行查找(最先查找局部作用域,最后查找内建作用域),但是不能反过来,比如不能在全局作用域查到位于局部作用域的变量。理解了Python3作用域访问的优先级就能理解“外部不能访问内部变量或函数”。
  在Python3里能引入新作用域的只有三种: 函数、类、模块 而其他代码块如:if/elif/else、for/while、try/except等不会引入新的作用域,因此外部可以直接访问到这些代码块内的变量。这点与c/c++有很大差异。
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
if True:
parameter1 = 1

for i in range(1):
parameter2 = 2

while(True):
parameter3 = 3
break

print(parameter1)
print(parameter2)
print(parameter3)

运行结果:

1
2
3
1
2
3

2.3 global和nonlocal关键字

  外部作用域无法访问内部作用域内的变量或函数这点不能改变(比如全局作用域不能访问到函数内的局部作用域内的变量),但是当内部作用域里想修改外部作用域变量值或访问外部函数时需要借助关键字globalnonlocal

2.3.1 global

  通过global声明全局变量或全局函数,使得内部作用域可修改全局变量的值或访问到全局函数。

2.3.1.1 global声明全局变量

  前面讲作用域的时候代码示例里演示了在函数内部访问(指读取值)了全局变量,若想在局部作用域修改全局变量的值则需要通过global声明全局变量。
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Parameter = 1

def Function1():
global Parameter #声明全局变量Parameter
Parameter = 2
print("Function1", Parameter)

def Function2():
print("Function2", Parameter) #局部作用域可读取全局作用域变量的值

def Function3():
Parameter = 3 #定义了局部变量,并不是修改全局变量的值
print("Function3", Parameter)

print(Parameter)
Function3() #没能修改全局变量的值
Function1() #修改全局变量的值
Function2() #读取全局变量的值

运行结果:

1
2
3
4
1
Function3 3
Function1 2
Function2 2

  需要注意的是如果内部作用域定义了和外部作用域同名的变量后再声明外部这个全局变量会报错。

2.3.1.2 global声明全局函数

  类似声明全局变量的用法,global也能声明全局函数。
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def Function1():                    #定义全局函数
print(1)

def Function2():
def Function1(): #嵌套函数定义,与全局函数同名
print(2)

def Function3():
global Function1 #声明全局函数
Function1() #调用全局函数

def Function4():
Function1() #调用的不是全局函数

Function3()
Function4()

Function2()

运行结果:

1
2
1
2

2.3.2 nonlocal

  类似global,想要修改嵌套作用域的变量或访问嵌套作用域的函数,则需要使用nonlocal进行声明。

2.3.2.1 nonlocal声明嵌套变量

  在嵌套定义的函数内可以读取到外层函数定义的变量值,但是若要修改外层函数变量的值则需要使用nonlocal进行声明变量。
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Parameter = 1                               #全局作用域变量

def Function1():
Parameter = 2 #嵌套作用域变量
def Function2():
nonlocal Parameter #声明嵌套作用域变量
Parameter = 5 #修改嵌套作用域变量
print(Parameter)

def Function3():
print(Parameter) #读取嵌套作用域变量值

def Function4():
Parameter = 3 #定义局部变量
print(Parameter)

Function4() #不能修改嵌套作用域变量
Function3() #读取的是嵌套作用域变量的值,符合查询的优先级
Function2() #修改嵌套作用域变量

Function1()

运行结果:

1
2
3
3
2
5
2.3.2.2 nonlocal声明嵌套函数

  类似声明嵌套变量的用法,nonlocal也能声明嵌套函数。
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def Function1():                                #全局函数
print("global", "Function1")

def Function2():
def Function1():
print("In", "Function1")

def Function3():
Function1() #访问的是嵌套函数

def Function4():
nonlocal Function1 #声明外层嵌套函数
Function1() #访问的是嵌套外层函数内定义的函数,而不是全局

Function3() #访问的是嵌套外层函数内定义的函数,而不是全局
Function4() #访问的是嵌套外层函数内定义的函数,而不是全局

Function2()

运行结果:

1
2
In Function1
In Function1