变量作用域 作用域指的是变量的有效范围。 变量并不是在哪个位置都可以访问的,访问权限取决于这个变量是在哪里赋值的,也就是在哪个作用域内的。
通常而言,在编程语言中,变量的作用域从代码结构形式来看,有块级、函数、类、模块、包等由小到大的级别。但是在Python中,没有块级作用域,
也就是类似if语句块、for语句块、with上下文管理器等等是不存在作用域概念的,他们等同于普通的语句。
if True : x = 1 print ( x) def func ( ) : a = 8 print ( a)
变量的作用域决定了程序的哪一部分可以访问哪个特定的变量名称。Python的作用域一共有4层,分别是:
L (Local) 局部作用域 E (Enclosing) 闭包函数外的函数中 G (Global) 全局作用域 B (Built-in) 内建作用域 global_var = 0 def outer ( ) : out_var = 1 def inner ( ) : inner_var = 2
前面说的都是变量可以找得到的情况,那如果出现本身作用域没有定义的变量,那该如何寻找呢?
Python以L –> E –> G –>B的规则查找变量,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,最后去内建中找。
如果这样还找不到,那就提示变量不存在的错误。例如下面的代码,函数func内部并没有定义变量a,可是print函数需要打印a,那怎么办?
向外部寻找!按照L –> E –> G –>B的规则,
层层查询,这个例子很快就从外层查找到了a,并且知道它被赋值为1,于是就打印了1。
a = 1 def func ( ) : print ( a)
全局变量和局部变量 定义在函数内部的变量拥有一个局部作用域,被叫做局部变量,定义在函数外的拥有全局作用域的变量,被称为全局变量。(类、模块等同理)
所谓的局部变量是相对的。局部变量也有可能是更小范围内的变量的外部变量。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
a = 1 def func ( ) : b = 2 print ( a) def inner ( ) : c = 3 print ( a) print ( b) print ( c)
global和nonlocal关键字 我们先看下面的例子:
total = 0 def plus ( arg1, arg2 ) : total = arg1 + arg2 print ( "函数内局部变量total= " , total) print ( "函数内的total的内存地址是: " , id ( total) ) return totalplus( 10 , 20 ) print ( "函数外部全局变量total= " , total) print ( "函数外的total的内存地址是: " , id ( total) )
global:指定当前变量使用外部的全局变量
total = 0 def plus ( arg1, arg2 ) : global total print ( "函数内局部变量total= " , total) print ( "函数内的total的内存地址是: " , id ( total) ) return totalplus( 10 , 20 ) print ( "函数外部全局变量total= " , total) print ( "函数外的total的内存地址是:" , id ( total) )
我们再来看下面的例子:
a = 1 print ( "函数outer调用之前全局变量a的内存地址: " , id ( a) ) def outer ( ) : a = 2 print ( "函数outer调用之时闭包外部的变量a的内存地址: " , id ( a) ) def inner ( ) : a = 3 print ( "函数inner调用之后闭包内部变量a的内存地址: " , id ( a) ) inner( ) print ( "函数inner调用之后,闭包外部的变量a的内存地址: " , id ( a) ) outer( ) print ( "函数outer执行完毕,全局变量a的内存地址: " , id ( a) )
如果你将前面的知识点都理解通透了,那么这里应该没什么问题,三个a各是各的a,各自有不同的内存地址,是三个不同的变量。
打印结果也很好的证明了这点:
函数outer调用之前全局变量a的内存地址: 4313327920 函数outer调用之时闭包外部的变量a的内存地址: 4313327952 函数inner调用之后闭包内部变量a的内存地址: 4313327984 函数inner调用之后,闭包外部的变量a的内存地址:4313327952 函数outer执行完毕,全局变量a的内存地址: 4313327920
那么,如果,inner内部想使用outer里面的那个a,而不是全局变量的那个a,怎么办?用global关键字?先试试看吧:
a = 1 print ( "函数outer调用之前全局变量a的内存地址: " , id ( a) ) def outer ( ) : a = 2 print ( "函数outer调用之时闭包外部的变量a的内存地址: " , id ( a) ) def inner ( ) : global a a = 3 print ( "函数inner调用之后闭包内部变量a的内存地址: " , id ( a) ) inner( ) print ( "函数inner调用之后,闭包外部的变量a的内存地址: " , id ( a) ) outer( ) print ( "函数outer执行完毕,全局变量a的内存地址: " , id ( a) )
运行结果如下,很明显,global使用的是全局变量a。
函数outer调用之前全局变量a的内存地址: 4360702256 函数outer调用之时闭包外部的变量a的内存地址: 4360702288 函数inner调用之后闭包内部变量a的内存地址: 4360702320 函数inner调用之后,闭包外部的变量a的内存地址: 4360702288 函数outer执行完毕,全局变量a的内存地址: 4360702320
那怎么办呢?使用nonlocal关键字!它可以修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量。将global a改成nonlocal a ,代码这里我就不重复贴了, 运行后查看结果,可以看到我们真的引用了outer函数的a变量。
函数outer调用之前全局变量a的内存地址: 4313852208 函数outer调用之时闭包外部的变量a的内存地址: 4313852240 函数inner调用之后闭包内部变量a的内存地址: 4313852272 函数inner调用之后,闭包外部的变量a的内存地址: 4313852272 函数outer执行完毕,全局变量a的内存地址: 4313852208
~~----------------------分割线----------------------
以下是练习题: 不要上机测试,请说出下面代码的运行结果:
1 a = 10 def test ( ) : a += 1 print ( a) test( )
2 name = 'jack' def outer ( ) : name= 'tom' def inner ( ) : name = 'mary' print ( name) inner( ) outer( )
3 name = 'jack' def f1 ( ) : print ( name) def f2 ( ) : name = 'eric' f1( ) f2( )
4 name = 'jack' def f2 ( ) : name = 'eric' return f1def f1 ( ) : print ( name) ret = f2( ) ret( )
第一题答案
这段代码有语法错误吗?a += 1相当于a = a + 1,按照赋值运算符的规则是先计算右边的a+1。但是,Python的规则是,如果在函数内部要 修改一个变量,那么这个变量需要是内部变量,除非你用global声明了它是外部变量。很明显,我们没有在函数内部定义变量a,所以会弹出局部变量在未 定义之前就引用的错误。
第二题答案:‘mary’ 第三题答案:‘jack’ 第四题答案:‘jack’
时间:2020/12/8 作者:Eamon