python LEGB变量作用域规则

先看下面这段代码,显然无法work. 因为代码试图在TestVariableScope()中引用一个没有被定义的变量a.所以必须报错,如下图-1.

不过如果你将第2行代码注释掉。代码就能跑通了,如图-2。

问题1来了:TestVariableScope.a 不是也没有被定义吗,为什么可以work呢?解释如下:先看代码第8行,TestVariableScope.a在SetVariable方法中被定义了,SetVariable()又 在TestVariableScope()前被调用。所以TestVariableScope()在被调用的时候TestVariableScope.a已经被定义了。

问题2来了:代码第7行,a也被定义了。为什么TestVariableScope()在引用a是报错呢。区别在于:a 只是SetVariable()中的一个local变量,TestVariableScope当然无法引用SetVariable中定义的局部变量了。因为违反了LEGB原则吗。TestVariableScope.a 就不一样了,他是一个全局变量哦。所以TestVariableScope当然可以访问这个全局变量了,完全不违反LEGB原则。

问题3来了:为什么TestVariableScope.a是个全局变量,而a却不是呢。因为python中函数皆是对象,而且是全局对象。TestVariableScope.a其实就是TestVariableScope这个全局对象下的一个变量而已,自然也是全局变量喽。 见图-3

按 Ctrl+C 复制代码
按 Ctrl+C 复制代码

图-1

图-2

图-3

以下关于LEGB, 引用自:https://magicalboy.com/python-scope-legb 

Python 的变量作用域和 LEGB 原则

在 Python 程序中创建、改变或查找变量名时,都是在一个保存变量名的地方进行中,那个地方我们称之为命名空间。作用域这个术语也称之为命名空间。

具体地说,在代码中变量名被赋值(Python 中变量声明即赋值,global 声明的只是变量的使用域)的位置决定了该变量能被访问的范围。函数定义了本地作用域,而模块定义的是全局作用域。这两个作用域之前有如下关系:

  • 每一个模块都是全局作用域。也就是说,创建于模块文件顶层的变量具有全局作用域,对于外部访问就成了一个模块对象的属性。
  • 全局作用域的作用范围仅限于单个文件。“全局”指的是在一个文件的顶层变量名对于这个文件而言是全局的。
  • 每次对函数的调用都创建了一个新的本地作用域。Python 中也有递归,即可以调用自身,每次调用都会创建五个新的本地命名空间。
  • 赋值的变量名除非声明为全局变量,否则均为本地变量。如果需要在函数内部对模块文件顶层的变量名赋值,需要在函数内部通过 global 语句声明该变量。
  • 所有的变量可归纳为本地、全局或者内置三种。范围分别为 def 内部,在一个模块的命名空间内部和预定义的 __builtin__ 模块提供的变量。

变量名解析:LEGB 原则

如果对以上内容有所迷惑的话,请看以下总结出的几条原则。在函数命名空间中:

  • 变量名引用分为三个作用域进行查找:首先是本地,然后是函数内(如果有的话),之后是全局,最后是内置。
  • 在默认情况下,变量名赋值会创建或者改变本地变量。
  • 全局声明将会给映射到模块文件内部的作用域的变量名赋值。
  • Python 的变量名解析机制也称为 LEGB 法则,具体如下: 当在函数中使用未确定的变量名时,Python 搜索 4 个作用域:本地作用域(L),之后是上一层嵌套结构中 def 或 lambda 的本地作用域(E),之后是全局作用域(G),最后是内置作用域(B)。按这个查找原则,在第一处找到的地方停止。如果没有找到,Python 会报错的

猜你喜欢

转载自blog.csdn.net/weixin_38246633/article/details/85311074