作者: Sam(甄峰) sam_code@hotmail.com
0.
命名空间(Namespace):
0.1:Namespace定义:
A namespace is a
mapping from names to objects.Most namespaces are currently
implemented as Python dictionaries。
一个Namespace就是一个名字和对象映射的区块或者说一张映射表。通过名字就可以找到对应的object.
提供了在项目中避免名字冲突的一种方法。各个NameSpace是独立的,不同Namespace的名字之间没有任何关联,所以一个Namespace内不能有重名,但不同的Namespace是可以有重名而没有任何影响的。
Python中常见的Namespace有:
A.
内置名称(built-in names).
Python的内置名称。如函数名,异常名等。可以认为是Python内置函数映射表。
B.
全局名称(global names).
模块中定义的名称,记录了模块的变量,包含函数,类,其它导入的模块,模块级的变量和常量。可以认为是模块映射表。
C.
局部名称(local names).
函数中定义的名称,记录的函数的变量,包含函数的参数和局部定义的变量。可以认为是此函数的映射表。
0.2:Namespace的查找顺序:
命名空间查找顺序:
假设我们要使用一个变量,则 Python 的查找顺序为:local
names -> global names
-> built-in names。
如果找不到此变量 ,它将放弃查找并引发一个 NameError 异常:
NameError: name
'xxxxx' is not defined。
0.3:Namespace的生命周期:
A. 内置名称(built-in names)
Namespace:在Python解释器启动时创建,在退出Python前不会删除。(因为函数等一直要用)
B. 全局名称(global
names) Namespace:在模块定义被读入时创建。
C. 局部名称(local
names) Namespace: 在这个函数被调用时被创建。在函数返回或抛出一个不在函数内部处理的错误时被删除。
一个程序运行时,回创建很多Namespace(可理解为Name-Object映射表)。它的主要作用就是在项目中避免名字冲突。
1.作用域(Scope):
作用域(Scope)是指Python一个文本区域,这个区域可以直接访问命名空间。
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有4种:
- L(Local):最内层,包含局部变量,比如一个函数/方法内部。
:包含了非局部(non-local)也非全局(non-global)的变量。(嵌套函数中常见)
存在函数嵌套时,一级一级往上层函数的命名空间查找
:当前脚本的最外层,比如当前模块的全局变量。
: 包含了内建的变量/关键字等,最后被搜索。
访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。
规则顺序: L
–> E
–> G
–> B。
在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。
Python
中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如
if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问
可以看到,在if 里定义的变量,在外面还是可以访问。这点与C++不同。
2.
全局变量和局部变量:
2.1:定义:
全局变量指在函数外定义的变量,它的name-object映射表为Global
Namespace. 生命周期和模块一样。除非程序结束运行,或者全局变量被删除(del x),才会被在Global
Namespace中删除。
局部变量指在函数内定义的变量。
此函数被调用时,创建name-object映射表即Local Namespace.
函数调用结束则删除。
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问
可以看到,函数内可以访问全局变量,也可以访问局部变量。
但当函数内定义一个与全局变量同名的变量时,其实是个全新的局部变量,与函数外同名变量无关。这里的关键是:当我们需要修改一个全局变量时,Python解释器会觉得疑惑,这到底是个修改,还是个定义。所以它统一成定义一个新Local变量。这个理由会在嵌套函数中再次看到,也会在List,
dict为何可以直接修改中再次看到。
2.2:函数内修改全局变量--global:
看到这里,就会有个疑问,既然函数内可以访问全局变量,那如何修改?
比如在上个例子中,
sub= num1/num2
这个语句被理解为创建一个新的局部变量。而不是修改全局变量的内容。
可以添加关键字 global。 申明这个变量为全局变量。
global关键字使用时,有两点需要注意:
A. 在函数内使用global申明为全局变量前,不能使用这个变量。
sub=1
global sub
sub=num1/num2
则会报错:
SyntaxError: name 'sub' is assigned to before
global declaration
B. 在全局作用域中,使用global申明,也会报错。
例如:
test = 4
global test
SyntaxError: name 'test' is assigned to before
global declaration
之前看到有文章说在
__main__内不需要global申明。其实可以用:“Python
中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如
if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问”
解释。
2.3:嵌套函数中,内层函数修改外层函数的局部变量---nonlocal:
使用nonlocal. 作用与global神似。
2.3.1: 嵌套函数访问外层函数的局部变量:
按照LEBG理论,完全可以访问:
可以看到,在嵌套函数内访问外层函数的局部变量没问题。
但当需要修改时,就遇到问题。这里的关键是:当我们需要修改一个外层函数局部变量时,Python解释器会觉得疑惑,这到底是个修改,还是个定义。所以它统一成定义一个新内层Local变量。这个理由会在List,
dict为何可以直接修改中再次看到。
2.3.2:嵌套函数中定义与外层函数局部变量相同的变量:
可以看到,在内层函数中定义新的变量,和外层函数是隔离的。
所以修改怎么办,这就是nonlocal的作用。
2.3.3:使用nonlocal关键字申明:
注意:nonlocal
后面跟着的变量必须是外层函数的局部变量,不能是全局变量。
3.
思考:为何在函数中修改List和dict内容,不需要global?
例如:
可以看到,list直接修改内容,使用append就可以在函数内修改全局变量。而其它变量不行。类似的,dict也可以。
这是因为:在函数中 使用dat = 4
这样的语句,是有歧义的,它既可以理解为:创建一个局部变量,也可以理解为修改全局变量。所以Python把这个行为统一理解为:创建一个局部变量。如果要修改,则需要加global关键字申明这其实是使用全局变量。
而list.append等语句,则没有这个问题,因为这既是为已经有的list添加一个项,而不可能是创建一个新的list.
所以,这里的关键是:这个行为是否是明确无歧义的。
Python变量的作用域
作者: Sam(甄峰) sam_code@hotmail.com
0. 命名空间(Namespace):
0.1:Namespace定义:
A namespace is a mapping from names to objects.Most namespaces are currently implemented as Python dictionaries。
一个Namespace就是一个名字和对象映射的区块或者说一张映射表。通过名字就可以找到对应的object. 提供了在项目中避免名字冲突的一种方法。各个NameSpace是独立的,不同Namespace的名字之间没有任何关联,所以一个Namespace内不能有重名,但不同的Namespace是可以有重名而没有任何影响的。
Python中常见的Namespace有:
A. 内置名称(built-in names). Python的内置名称。如函数名,异常名等。可以认为是Python内置函数映射表。
B. 全局名称(global names). 模块中定义的名称,记录了模块的变量,包含函数,类,其它导入的模块,模块级的变量和常量。可以认为是模块映射表。
C. 局部名称(local names). 函数中定义的名称,记录的函数的变量,包含函数的参数和局部定义的变量。可以认为是此函数的映射表。
0.2:Namespace的查找顺序:
命名空间查找顺序:
假设我们要使用一个变量,则 Python 的查找顺序为:local names -> global names -> built-in names。
如果找不到此变量 ,它将放弃查找并引发一个 NameError 异常:
NameError: name 'xxxxx' is not defined。
0.3:Namespace的生命周期:
A. 内置名称(built-in names) Namespace:在Python解释器启动时创建,在退出Python前不会删除。(因为函数等一直要用)
B. 全局名称(global names) Namespace:在模块定义被读入时创建。
C. 局部名称(local names) Namespace: 在这个函数被调用时被创建。在函数返回或抛出一个不在函数内部处理的错误时被删除。
一个程序运行时,回创建很多Namespace(可理解为Name-Object映射表)。它的主要作用就是在项目中避免名字冲突。
1.作用域(Scope):
作用域(Scope)是指Python一个文本区域,这个区域可以直接访问命名空间。
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python 的作用域一共有4种:
:包含了非局部(non-local)也非全局(non-global)的变量。(嵌套函数中常见)
存在函数嵌套时,一级一级往上层函数的命名空间查找
:当前脚本的最外层,比如当前模块的全局变量。
: 包含了内建的变量/关键字等,最后被搜索。
访问一个变量,会从内到外依次访问所有的作用域直到找到,否则会报未定义的错误。
规则顺序: L –> E –> G –> B。
在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内置中找。
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问
可以看到,在if 里定义的变量,在外面还是可以访问。这点与C++不同。
2. 全局变量和局部变量:
2.1:定义:
全局变量指在函数外定义的变量,它的name-object映射表为Global Namespace. 生命周期和模块一样。除非程序结束运行,或者全局变量被删除(del x),才会被在Global Namespace中删除。
局部变量指在函数内定义的变量。 此函数被调用时,创建name-object映射表即Local Namespace. 函数调用结束则删除。
定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问
可以看到,函数内可以访问全局变量,也可以访问局部变量。 但当函数内定义一个与全局变量同名的变量时,其实是个全新的局部变量,与函数外同名变量无关。这里的关键是:当我们需要修改一个全局变量时,Python解释器会觉得疑惑,这到底是个修改,还是个定义。所以它统一成定义一个新Local变量。这个理由会在嵌套函数中再次看到,也会在List, dict为何可以直接修改中再次看到。
2.2:函数内修改全局变量--global:
看到这里,就会有个疑问,既然函数内可以访问全局变量,那如何修改?
比如在上个例子中,
sub= num1/num2
这个语句被理解为创建一个新的局部变量。而不是修改全局变量的内容。
可以添加关键字 global。 申明这个变量为全局变量。
global关键字使用时,有两点需要注意:
A. 在函数内使用global申明为全局变量前,不能使用这个变量。
sub=1
global sub
sub=num1/num2
则会报错:
SyntaxError: name 'sub' is assigned to before global declaration
B. 在全局作用域中,使用global申明,也会报错。
例如:
test = 4
global test
SyntaxError: name 'test' is assigned to before global declaration
之前看到有文章说在 __main__内不需要global申明。其实可以用:“Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这些语句内定义的变量,外部也可以访问” 解释。
2.3:嵌套函数中,内层函数修改外层函数的局部变量---nonlocal:
使用nonlocal. 作用与global神似。
2.3.1: 嵌套函数访问外层函数的局部变量:
按照LEBG理论,完全可以访问:
可以看到,在嵌套函数内访问外层函数的局部变量没问题。
但当需要修改时,就遇到问题。这里的关键是:当我们需要修改一个外层函数局部变量时,Python解释器会觉得疑惑,这到底是个修改,还是个定义。所以它统一成定义一个新内层Local变量。这个理由会在List, dict为何可以直接修改中再次看到。
2.3.2:嵌套函数中定义与外层函数局部变量相同的变量:
可以看到,在内层函数中定义新的变量,和外层函数是隔离的。
所以修改怎么办,这就是nonlocal的作用。
2.3.3:使用nonlocal关键字申明:
注意:nonlocal 后面跟着的变量必须是外层函数的局部变量,不能是全局变量。
3. 思考:为何在函数中修改List和dict内容,不需要global?
例如:
可以看到,list直接修改内容,使用append就可以在函数内修改全局变量。而其它变量不行。类似的,dict也可以。
这是因为:在函数中 使用dat = 4 这样的语句,是有歧义的,它既可以理解为:创建一个局部变量,也可以理解为修改全局变量。所以Python把这个行为统一理解为:创建一个局部变量。如果要修改,则需要加global关键字申明这其实是使用全局变量。
而list.append等语句,则没有这个问题,因为这既是为已经有的list添加一个项,而不可能是创建一个新的list.
所以,这里的关键是:这个行为是否是明确无歧义的。