跳到主要内容

python变量的作用域

命名空间

x = 1, 1存在内存中,x存在哪?命名空间。

python里面有很多名字空间,每个地方都有自己的名字空间,互不干扰,不同空间中的两个相同名字的变量之间没有任何联系。

名称空间有4种:LEGB

  • locals:函数内部的名字空间,一般包括函数的局部变量以及形式参数。

  • enclosing function:在嵌套函数中外部函数的名字空间,若fun2嵌套在fun1里,对fun2来说,fun1的名字空间就enclosing。

  • globals:当前的模块空间,模块就是一些py文件。也就是说,globals()类似全局变量。

  • builtins: 内置模块空间,也就是内置变量或者内置函数的名字空间,

    print(dir(内置变量或内置函数))可查看包含的值。

不同变量的作用域不同就是由这个变量所在的名称空间决定的。

作用域即范围

  • 全局范围:全局存活,全局有效
  • 局部范围:临时存活,局部有效

查看作用域方法 globals(),locals()

变量作用域说明

python的变量作用域划分

  1. 局部作用域(L)
  2. 闭包函数外到函数中(E)
  3. 全局作用域(G)
  4. 内建作用域(B)

变量查找的规则

L->E->G->B

首先在自身作用域中查找有没有同名变量,找不到的话依次向上级作用域中查找,不会向低级作用域中查找。

找到了之后,便停止搜索,如果最后没有找到,则抛出在NameError的异常。

注意:Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这这些语句内定义的变量,外部也可以访问。

跨域修改变量值

在局部作用域中直接给变量赋值,相当于在当前作用域定义了一个局部变量。

  1. 在函数中修改全局变量的值,需要将变量声明为全局变量
num = 1
def func():
global num
num = 2
func()
  1. 修改嵌套作用域中变量的值,需要将其声明成嵌套作用域中的变量
num = 0
def func1():
num = 1
def func2():
nonlocal num
num = 2
func2()
print(num) # 2
func1()
print(num) # 1
  1. 不能访问以及修改同等级作用域的变量

闭包

关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。也就是说,内部函数会在外部函数返回后被执行。而当这个内部函数执行时,它仍然必需访问其外部函数的局部变量、参数以及其他内部函数。这些局部变量、参数和函数声明(最初时)的值是外部函数返回时的值,但也会受到内部函数的影响。

# 使用闭包可以突破作用域链,在函数体外使用函数体中定义的变量
def outer():
name = 'alex'
def inner():
print("在inner里打印外层函数的变量",name)
return inner # 注意这里只是返回inner的内存地址,并未执行
f = outer() # 接收的是inner的内存地址
print(f) # 输出:<function outer.<locals>.inner at 0x0136D4F8>
f() # 相当于执行的是inner()

pythonbibao2208

注意此时outer已经执行完毕,正常情况下outer里的内存都已经释放了,但此时由于闭包的存在,我们却还可以调用inner, 并且inner内部还调用了上一层outer里的name变量。这种粘粘糊糊的现象就是闭包。

闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域

优点:避免污染全局环境,这样就可以在函数体外使用函数体中定义的变量

缺点:数据会长期驻留在内存中,造成内存极大的浪费

注意:尽量避免使用闭包

可能遇到闭包的坑

def func():
arr = []
for i in range(3):
def f():
print("-----------", i)
arr.append(f)
return arr

li = func()
li[0]()
li[1]()
li[2]()