Python作用域分类与LEGB规则详解
局部作用域是函数内部的私有空间
在函数内部对变量进行定义时, 该变量归属于局部作用域。它仅仅在当前函数执行之际存在着, 而当函数结束以后就会被予以销毁。举例来说, 要是你撰写一个用于计算折扣的函数, 在其中定义了一个临时变量, 这个变量唯有在函数内部才能够得以使用。
在函数外部直接去访问属于局部的变量, 将会触发NameError错误, 比如说定义一个def foo()函数, 其中让x等于10, 接着在函数外面进行print(x)操作, 此时解释器就会提示你x并没有被定义, 这便是局部作用域独有的那种隔离特性, 它能够确保函数之间的变量不会相互产生干扰。
同名变量的遮蔽机制要特别注意
若是于函数内部定义了一个与全局变量名字相同的变量, 那么在函数内部便会运用局部版本, 而全局变量则被暂且屏蔽。这般情形于实际进行开发之时常常会引发出漏洞, 比如你在全局定义了一个total等于0, 接着在函数里又写下一个total等于100, 然而函数外部的total依旧是0。
作用域解析所具备的自然结果便是遮蔽机制, 解释器依据LEGB顺序去查找变量之际, 一旦在局部找到便会直接加以使用, 而不会再朝着上方进行查找, 所以有时候你觉得自己是在对全局变量作出修改, 可实际上却是在创建全新的局部变量。
def my_function(): local_var = "I am local" # 定义一个局部变量 print(local_var) # 在函数内部访问局部变量 my_function() # 输出: I am local # 尝试在函数外部访问局部变量会导致NameError # print(local_var) # 这会抛出NameError # 变量遮蔽示例 x = "I am global" def another_function(): x = "I am local and shadowing the global x" print(x) # 访问局部作用域中的x print(x) # 访问全局作用域中的x another_function() # 访问并打印被遮蔽的局部变量x print(x) # 再次访问全局作用域中的x嵌套作用域让内部函数访问外部变量
当于一个函数之内再度定义函数之际, 内部函数能够存取外部函数里的变量。此乃嵌套作用域的关键特性。举例而言, 设有一个外部函数make_multiplier , 于其内部定义了一个内部函数multiply , 那么multiply能够读取外部函数的参数。
def outer_function(text): def inner_function(): # 这里可以访问 outer_function 中的 text 变量 print(text) # 调用内部函数 inner_function() # 调用外部函数 outer_function("Hello, nested scope!")写装饰器或者工厂函数之际, 这种机制尤为有用。不过请注意, 访问是单向的, 内部函数无法直接对外部函数的变量予以修改。要是需作修改, 可借助nonlocal关键字声明, 如此一来, 内部函数便能对外部函数的变量值进行修改了。
def outer_function(text): def inner_function(): # 这里可以访问 outer_function 中的 text 变量 print(text) # 返回内部函数,形成闭包 return inner_function # 调用外部函数并获取闭包 my_closure = outer_function("Hello, closure!") # 稍后调用闭包 my_closure() # 输出: Hello, closure!闭包是嵌套作用域的高级应用
包含这样种情况, 闭包所指的是, 一个函数记住了它外部作用域当中的变量, 哪怕外部函数已然执行完毕, 也仍能记住它。举例来说, 你撰写了一个名为outer的函数, 该函数返回inner函数, 而在inner函数里运用了outer的变量text。当你对outer进行调用之后, 返回的inner函数依旧能够访取text。
# 这是一个全局变量 global_var = "I am a global variable" def func(): # 这里可以访问全局变量 print(global_var) func() # 输出: I am a global variable此特性于撰写回调函数、开展延迟计算或者维持状态之际极为实用, 比如说你若欲打造一个计数器, 每回调用便递增1, 借助闭包即能够达成, 无需全局变量。归属于闭包的变量被留存于函数对象的__closure__属性之内, 因而不会遭受垃圾回收。
global_var = "I am a global variable" def func(): # 尝试修改全局变量(但实际上是创建了一个局部变量) global_var = "I am trying to change the global variable, but I failed" print(global_var) # 输出: I am trying to change the global variable, but I failed func() print(global_var) # 输出: I am a global variable,说明全局变量未被修改全局作用域是整个模块的公共空间
global_var = "I am a global variable" def func(): global global_var # 声明要修改的是全局变量 global_var = "I am now a changed global variable" func() print(global_var) # 输出: I am now a changed global variable模块顶层所定义的变量以及函数, 全都归属于全局作用域, 它们于整个模块范围之内均可进行访问, 暂且除去被局部变量所遮蔽的情况, 比如说你于一个Python文件的开头之处定义了PI等于3.14, 那么在该文件里的所有函数均能够读取到这个PI。
# 尝试访问内置变量 print(len([1, 2, 3])) # 输出: 3,len是内置函数 # 尝试覆盖内置变量(不推荐) len = "I am not a function anymore" print(len([1, 2, 3])) # 这将引发TypeError,因为len现在是一个字符串 # 还原内置变量(不推荐这样做,但展示了如何恢复) del len print(len([1, 2, 3])) # 再次输出: 3,因为len内置函数被“恢复”了想在函数里头对全局变量做到修改, 那得倚靠global关键字才行。要是你直接写下个x = 10, 那仅仅是弄出了个局部的x罢了。要变动全局范畴的x, 那就得先去声明global x。全局变量虽说便利, 可要是用得过多的话, 会致使代码在调试以及维护方面变得困难重重, 所以建议仅在必要的情节采用它标点符号。
内置作用域是Python自带的函数和对象
Python所具备的内置作用域涵盖了len、print、int、str这些函数以及类型。这些被称为内置对象的, 在程序的任意一处地方均能够直接去使用, 并不需要进行任何板块的导入。好似你在任意时刻都能够去调用len("hello") , 解释器会在这个内置作用域里头寻找到len函数的。
import builtins # 现在可以访问builtins模块中的对象了 print(builtins.len([1, 2, 3])) # 输出: 3一旦你不留意地把内置函数予以重新赋值, 哪怕是像将len赋值为100这般, 那么紧接着调用len()便会产生报错情况。若要使之恢复, 能够采取del len的操作或者发动重启解释器。纵然在技术层面能够对内置对象作出修改, 然而强烈地不建议如此去做, 除非你正处于开展单元测试或者临时性地修补特定bug的状况之下。
# 定义一个全局变量 global_var = "I am a global variable" def outer_function(): # 定义一个嵌套作用域变量,仅在outer_function内部可见 nested_var = "I am a nested variable" def inner_function(): # 定义一个局部变量,仅在inner_function内部可见 local_var = "I am a local variable" # 访问嵌套作用域变量 print(nested_var) # 输出: I am a nested variable # 尝试访问全局变量(未使用global关键字直接访问) print(global_var) # 输出: I am a global variable # 尝试访问内置作用域中的内置函数 print(len("hello")) # 输出: 5,展示了内置作用域的使用,尽管不是直接修改 # 访问局部变量 print(local_var) # 输出: I am a local variable # 调用内部函数,以展示嵌套作用域 inner_function() # 尝试直接访问嵌套作用域变量(会失败) # print(nested_var) # NameError: name 'nested_var' is not defined # 访问全局变量 print(global_var) # 输出: I am a global variable # 修改全局变量 global_var = "I am a modified global variable" print(global_var) # 输出: I am a modified global variable # 调用外部函数,以展示全局和嵌套作用域 outer_function() # 再次尝试访问局部变量(会失败) # print(local_var) # NameError: name 'local_var' is not defined # 尝试覆盖内置函数(不推荐,仅作演示) original_len = len # 保存原始len函数 len = "I overwrote len!" # 尝试覆盖len(不推荐这样做) # 尝试使用被覆盖的len(会失败,因为现在是字符串) try: print(len([1, 2, 3])) # TypeError: 'str' object is not callable except TypeError as e: print(f"Caught an error: {e}") # 捕获并打印错误 # 恢复原始len函数 len = original_len print(len([1, 2, 3])) # 输出: 3再度询问一回问题: 于真实项目里头, 你有着遭遇过因作用域理解出现差错从而致使的程序故障现象吗? 诚挚邀请于评论分区予以分享你的经历情形, 进行点赞动作并收藏这篇目文章, 使得更多Python开发者能够避开此类陷阱状况。
I am a global variable I am a modified global variable I am a nested variable I am a modified global variable 5 I am a local variable Caught an error: 'str' object is not callable 3