深入解析Python变量作用域与LEGB查找规则
本文深入解析Python变量作用域机制,详细阐述LEGB(Local, Enclosing, Global, Built-in)查找规则。通过代码示例说明局部、嵌套、全局及内置作用域的区别,重点讲解Global与Nonlocal关键字在修改外部变量时的具体用法及常见报错原因,帮助开发者避免NameError并优化代码结构。
Python变量作用域的基本特点
特点
Python采用静态作用域机制,这意味着变量名的有效访问范围由其在源代码中的定义位置决定。需要注意的是,并非所有代码块都会创建新的作用域;只有当变量定义在模块(Module)、类(Class)或函数(def)内部时,才会形成特定的作用域边界。
1. 函数内部的变量,函数外部不能访问
def func():
variable = 100
print(variable)
print(variable) # name 'variable' is not defined
2. 函数上层的变量(标量)只能读取,不能再次定义或初始化
def counter1():
n = 0
def compute():
n = n + 1 # n为标量(数值,字符串,浮点数),Python程序会因为“如果内部函数有引用外部函数的同名变量或者全局变量,并且对这个变量有修改.那么python会认为它是一个局部变量,又因为函数中没有n的定义和赋值,所以报错
# y = n + 1 # 更改为y就没事
# return y
return n
return compute
variable = 300
def test_scopt():
print(variable) # 此时调用局部变量variable并有没绑定到一个内存对象(没有定义和初始化,即没有赋值)。本质上还是遵循的LEGB法则
variable = 200 #因为这里,前面调用过一次,所以variable就变为了局部变量
# print(variable) # 写在下面就没问题,因为variable是新的局部变量,而不是重新被定义,却没有绑定
test_scopt()
Python模块代码在执行前不经过预编译,但模块内的函数体在运行前会进行预编译。因此,无论变量绑定发生在作用域的哪个位置,编译器都能识别其存在。尽管Python属于静态作用域语言,但变量名的解析是动态进行的,作用域相关的问题通常在程序运行时才会暴露。
3. list、dict等复合变量内部的值都可以引用并更改
def counter():
n = [0]
def compute():
n[0] += 1 # 更改的是n里面的第一个值,不是更改n
return n[0]
return compute
func = counter()
func() # 1
func() # 2
func() # 3
4. 使用global声明全局变量。若需在局部作用域中修改全局变量,必须先在局部作用域内通过global关键字声明该变量。
def counter1():
n = 0
def compute():
global n # 如果在局部要对全局变量修改,需要在局部也要先声明该全局变量,但此处也会报错,因为没有全局变量n
n += 1
return n
return compute
# right
def counter1():
global n
n = 0
def compute():
global n
n += 1
return n
return compute
5. nonlocal关键字用于在函数或其他嵌套作用域中引用并修改外层(非全局)变量。
def make_counter():
count = 0
def counter():
nonlocal count # 使用外层非全局变量
count += 1
return count
return counter
Python中的四种作用域类型
作用域的类型
在Python中,使用变量前无需显式声明,但在使用前必须将其绑定到具体的内存对象(即完成定义或赋值)。这种绑定操作会在当前作用域引入新变量,并屏蔽外层作用域中同名的变量。
L (Local) 局部作用域
局部变量是指在def关键字定义的函数块内部定义的变量。每次调用函数时,都会创建一个新的局部作用域。Python支持递归,即函数调用自身,每次递归调用都会生成一个新的局部命名空间。除非特别声明为全局变量,否则函数内的变量默认均为局部变量。若需在函数内部定义全局变量,可使用global关键字。局部作用域类似于栈结构,具有临时性,仅当创建该作用域的函数处于活动状态时才存在。因此,建议尽量减少全局变量的使用,因为它们在整个模块运行期间占用内存空间。 注意:若需在函数内部对全局变量赋值,必须通过global语句声明该变量为全局变量。
E (Enclosing) 嵌套作用域
E作用域同样包含在def关键字定义的函数中。E与L是相对概念,对于更上层的函数而言,E也是L。与L的区别在于,对于某个函数,L是定义在该函数内部的局部作用域,而E是定义在该函数上一层父级函数的局部作用域。E作用域主要是为了实现Python的闭包功能而设计的。
G (Global) 全局作用域
全局作用域指在模块层次中定义的变量,每个模块本身就是一个全局作用域。也就是说,在模块文件顶层声明的变量具有全局作用域。从外部视角看,模块的全局变量表现为该模块对象的属性。 注意:全局作用域的范围仅限于单个模块文件内。
B (Built-in) 内置作用域
内置作用域包含系统固定模块中定义的变量,例如预定义在builtins模块内的变量。
LEGB变量查找法则与示例
作用域链:变量名解析LEGB法则
变量名搜索的优先级顺序为:局部作用域 > 嵌套作用域 > 全局作用域 > 内置作用域。 LEGB法则规定:当在函数中使用未确定的变量名时,Python会按优先级依次搜索这四个作用域,以确定变量名的含义。首先搜索局部作用域(L),接着是上一层嵌套结构中def或lambda函数的嵌套作用域(E),然后是全局作用域(G),最后是内置作用域(B)。遵循此查找原则,一旦在第一个匹配的作用域中找到变量即停止搜索。若未找到任何匹配项,则触发NameError错误。
example 1
name = "lzl"
def f1():
print(name)
def f2():
name = "eric"
f1()
f2() # 在函数未执行之前,作用域链就已经形成了,此时f1()的上一级应该name = 'lzl'
example 2
def scope_test():
def do_local():
spam = "local spam" # 此函数定义了另外的一个spam字符串变量,并且生命周期只在此函数内。此处的spam和外层的spam是两个变量,如果写出spam = spam + “local spam” 会报错
def do_nonlocal():
nonlocal spam # 使用外层的spam变量 test spam
spam = "nonlocal spam"
def do_global():
global spam
spam = "global spam"
spam = "test spam"
do_local()
print("After local assignmanent:", spam) # test spam
do_nonlocal()
print("After nonlocal assignment:",spam) # nonlocal spam
do_global()
print("After global assignment:",spam) # nonlocal spam ???? 先找是本地变量,找到的本地变量已经在do_nonlocal()里面改变了所以输出的是nonlocal spam
scope_test()
print("In global scope:",spam) # global spam
以上就是深入了解Python变量作用域的详细内容,更多关于Python变量作用域的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









