人生有两件事最难得:一是知足,二是感恩。知足,会让你看到别人的优点,拥有平和的心态,收获充实的生活;感恩,会让你懂得尊重和包容,增长人生的智慧,获得他人的认可。站得多高,看的就多远,你的目光所及就是你的世界;茫茫人海,匆匆旅途,重要的从来都不是结果。
一般不建议使用global(内层函数都可以看到全局变量),相当于常量,学习它是为了深入理解变量作用域;
函数体内部出现变量定义, 代表意义重大;
本章总结:
- global 优先放在前面;在内部不能先被赋值;函数定义的全局变量值会随着函数体内部变量的变化而改变;
- global 内部函数体变量的声明定义表示引用全局定义: 不在内外层之间有联系; 只影响当前作用域;内外层作用域是可以看见全局变量作用域;
- global 全局变量不建议经常使用;——global全局变量定义后,所有内部函数都可以看到,调用;传递在所有内部函数体之间;
- global 在不同作用域之间的声明,必须要有定义,可以在使用的时候再定义赋值;
- 在嵌套函数中,才谈闭包;指的是一个概念:内存函数引用了外层函数的自由变量; 闭包特性:内部定义的变量引用了外部变量; 被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
- 变量定义以后,全局变量与内部变量没有关系;
- nonlocal声明只在内外部变量之间起作用;用的是外部变量的定义,本地变量没有定义;
- 函数传形参 相当于 局部变量定义:
- nonlocal 逐级向外 查找,绝对不会跑出最外层的封装,去全局作用域中查找;
参考:Python函数式(九)——闭包
1. LEGB(Local, Enclosing, Global, Built-ins)
Python会按照LEGB(Local, Enclosing, Global, Built-ins)沿着内层局部命名空间->外层局部命名空间->全局命名空间->内建命名空间的顺序去寻找一个标识符的定义,也就是说,定义于局部命名空间的标识符会覆盖全局命名空间的同名标识符,定义于全局命名空间的标识符会覆盖同名的内建命名空间标识符:
Python中的作用域分4种情况:
L:local,局部作用域,即函数中定义的变量(局部变量);
E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的;
G:globa,全局变量,就是模块级别定义的变量;
B:built-in,系统固定模块里面的变量,比如int, bytearray等。搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB。
1. 局部命名空间在函数调用结束后就会消失,因而,局部变量无法在全局命名空间中使用(上例中的x),但是全局变量可以在函数内访问得到。
a = 1
abs = 2
def local():
b = 1
a = 3
print(a)
local()
# 3
print(a)
# 1
print(abs)
# 2
- Python解释器执行程序之前,Python已经预先将函数内的标识符a指定为局部变量,它会覆盖掉全局命名空间中的a;在执行时,第一次print时a还没有和对象3绑定(仅仅知道它是一个局部变量),所以会产生错误。
a = 1
def func():
print(a)
a = 3
print(a)
func()
UnboundLocalError: local variable 'a' referenced before assignment
总结:
1.全局变量和内部、外部变量名不能相同(不能都赋值);容易冲突(这样好理解)
2.变量作用域的提升
如何避免上述问题呢?答案是将函数设计为无状态形式,将所有需要用到的外部变量全部作为参数传递给函数。不
python中的函数在运行前会经过预编译,函数内部的所有变量的声明都会被提到函数的开始处,此时并未开辟空间,只有真正赋值后才在内存中开辟空间,变量使用时,必须在内存中已经开辟了空间。
2.1 global
global不仅能绑定到已经存在的全局变量上,还能创造新的全局变量:
global的作用是将局部变量提升到变成全局变量,不过提升之前不能赋值。
格式如下: 优先放到前面;生产环境中,相当于常量,建议少使用!
总结:
1. 使用全局变量;优先放在前面;在内部不能先被赋值 ;
2. global x x=10 ; 等于在全局变量 x=10 ,不是外部变量 ;
3. 生产环境中,相当于常量,建议少使用
a = 1
def func():
global a ## 使用全局变量;优先放在前面;在内部不能先被赋值 ;SyntaxError: name 'a' is assigned to before global declaration
print(a)
a = 2
print(a)
func()
print(a)
--------------
1
2
2 # global全局变量在函数体内部的改变也是全局变量的改变;
def foo():
z = 100
def bar():
global z
z = z + 1
print(1,z)
bar()
foo()
print(2,z)
--------------------------------
1 100
NameError: name 'z' is not defined
2.两层global 嵌套
def foo():
global z # 相当于外部的 z = 100
z = 100
def bar():
global z
z = z + 1
print(1,z)
bar()
foo()
print(2,z)
------------------------------
1 100
2 201
3.多层内存嵌套全局变量,最后赋值变量;
def foo():
global z #相当于外部的 z = 100
#z = 100
def bar():
global z
z = 200
z = z + 1
bar()
print(1,z)
foo()
print(2,z)
--------------------------
1 201
2 201
4. 查看命名空间中存在哪些标识符
print(dir()) # 看命名空间中存在哪些标识符呢
---------------
总结:
1. 多层内存嵌套循坏中,声明全局变量后,程序是按照一步步运行的;
2. 尽量不要使用全局变量; global
3. global x x=10 ; 等于在外部 x=10 ;
4.
2.2 闭包(Closure) ( 嵌套函数中使用)
自由变量:未在本地作用域中定义的变量。例如定义在内层函数外的外层函数的作用域中的变量;
闭包(Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是内存函数引用了外部函数-自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
定义闭包格式
1.返回 return 函数对象,而不是调用函数。
2.实质就是函数体中, 内部变量 调用 外部变量;
让它直接返回函数对象,而不是调用嵌套函数:
def outer(msg):
def inner():
print(msg)
return inner # 返回函数对象,而不是调用函数。
func = outer('Hello')
func()
Hello
当外部函数 outer(msg) 被调用时,一个闭包 inner() 就形成了,并且它持有自由变量 msg。这意味着,当函数 outer(msg) 的生命周期结束之后,变量 msg 的值依然会被记住,不妨来试试:
# 返回里面Inc函数;
def foo():
z = 100
def bar():
z = z + 1
return z
return bar()
foo() # 返回里面Inc函数;
z = 100
print(foo())
--------------------------------------------------------------------
可变类型的函数定义: (与一般的有区别)
def counter():
c = [0]
def inc():
c[0] += 1
return c[0]
return inc # 复杂类型、变量内存地址、引用
foo = counter() # 返回 函数inc foo=>inc inc() foo()
print(foo(),foo())
c=100 # 变量定义以后,全局变量与内部变量没有关系;
print(foo())
--------------------------------------
1 2
3
2.3 nonlocal
从 2.x 开始,Python 通过词法作用域支持闭包。然而,在特性的最初实现中“有一点小问题”。之所以这么说,是因为在 2.x 中,闭包无法更改 nonlocal 变量(只读的),但从 3.x 起,该问题已经被解决了,详见 PEP-3104(https://www.python.org/dev/peps/pep-3104/)。
词法作用域(Lexical Scoping):变量的作用域在定义时决定,而不是在执行时决定。也就是说,词法作用域取决于源码,通过静态分析就能够确定,因此,词法作用域也叫做静态作用域
因为函数内可以定义新的函数,因而在Python中,局部命名空间是可以嵌套的,即一个局部命名空间中包含另一个局部命名空间:
为了使内层函数能够使用到外层的局部变量,我们需要使用关键字nonlocal来声明一下,这样,内层的标识符就指向了外层的对象:
总结:
1. nonlocal声明只在内外部变量之间起作用;
2. c 和函数的形参c 完全是两码事;
c=100
nonlocal c
def outer():
name = 'Z'
print('outer: {}'.format(name))
def inner():
# 内部函数访问外部函数定义的变量
nonlocal name
print('inner: {}'.format(name))
# 内部函数访问外部函数定义的变量的值
name = 'X'
print('inner: {}'.format(name))
inner()
print('outer: {}'.format(name))
outer()
--------------------------------------------
outer: Z
inner: Z
inner: X
outer: X
2. 内层的标识符就指向了外层的对象,可以看到,外层的局部变量也被修改了 a=3
def func():
a = 1
def inner():
nonlocal a
print(a)
a = 3
print(a)
inner()
print(a)
func()
# 1
# 3
# 3
传参数相当于定义:
def counter(c): #传参数相当于定义:
#c = 0
def inc():
nonlocal c
c += 1
return c
return inc
foo = counter(100)
print(foo())
print(foo())
---------------------
101
102