概述
python作用域基础
在默认情况下,一个函数内赋值的所有变量都与该函数的命名空间相关联,这意味着:
- 在def内赋予的变量名只能够被def内的代码使用;
- 在def内赋值的变量名与在def外复制的变量名不冲突,即使是相同的变量名;
在任何情况下,一个变量的作用域总是由它在代码中被赋值的位置决定的。变量可在3个不同的地方被赋值,分别对应3中不同的作用域:
- 如果一个变量在def内赋值,它对于该函数而言是局部的;
- 如果一个变量在一个外层的def中赋值,对于内层的函数来说,它是非局部的;
- 如果一个变量在所有的def外,它对整个文件来说是全局的;
作用域细节
函数定义了局部作用域,而模块定义了具有如下属性的全局作用域:
- 外围模块的全局作用域;
- 全局作用域的作用范围仅限于单个文件;
- 赋值的变量名除非被声明为global或nonlocal,否则均为局部变量;
- 所有其他的变量名都是外层函数的局部变量,全局变量或内置变量;
- 函数的每一次调用都会创建一个新的局部作用域;
变量名解析:LEGB规则
对于一条def语句:
- 在默认情况下,变量名赋值会创建或改变局部变量;
- 变量名引用至多可以在四种作用域内进行查找:首先是局部、其次是外层的函数、之后是全局、最后是内置;
- 使用global和nonlocal语句声明的名称将赋值的变量名分别映射到外围的模块和函数的作用域;
python的变量名解析机制有时称为LEGB规则,这也是由作用域的命名而来的:
- 当在函数中使用为限定的变量名时,python将查找4个作用域并在第一次找到该变量名的地方停下来:首先是局部作用域(L),其次是向外一层的def或lambda的局部作用域(E),之后是全局作用域(G),最后是内置作用域(B)。如果变量名在这些作用域中没有找到,python将会报错;
- 当你在函数中给一个变量名赋值时,python便会创建或改变局部作用域的变量名,除非该变量名已经在该函数中被声明为全局变量;
- 当你在所有函数的外面给一个变量名赋值时,此时的局部作用域就是全局作用域;
作用域实例
# Global scope
X = 99
def func(Y):
# Local scope
Z = X + Y
return Z
全局变量名:X,func
局部变量名:Y,Z
global语句
关于global语句的总结:
- 全局变量是在外层模块文件的顶层被赋值的变量名;
- 全局变量如果是在函数内被赋值的话,必须经过声明;
- 全局变量名在函数的内部不经过声明也可以被引用;
程序设计:最少化全局变量
尽管global语句很有用,然而在def内部赋值的变量名默认为局部变量,通常这时做最好的约定。将其改为全局变量会引发一些软件工程问题,由于变量的值取决于函数调用的顺序,而函数自身是任意循序进行排序的,导致程序调试或理解起来变得很困难。所以,在不熟悉编程的情况下,最好尽可能避免使用全局变量——它们容易使得程序变得难以理解和重用,而且会在所存储的数据不止一份的情形下失效。
程序设计:最小跨文件的修改
这是另一个和作用域相关的设计问题:尽管能直接修改另一个文件中的变量,但是往往我们都不会这样做。
# first.py
X = 99
# second.py
import first
print(first.X)
first.X = 88
第一个模块文件定义了变量X,这个变量在第二个文件中被打印并通过赋值修改了。这样使得两个文件之间产生过强的耦合:因为它们都与变量X的值相关,如果没有其中一个文件的话很难理解或重用另一个文件。
上面程序的编写方式是极不可取的,在文件之间进行通信最要的办法就是通过调用函数、传递参数,然后得到其返回值;
作用域和嵌套函数
实际上,嵌套是一种代码写法上的表述:嵌套的作用域对应于程序源代码文本中物理上和句法上的嵌套代码结构;
嵌套作用域的细节
- 一个引用首先在局部(函数内)作用域查找变量名;之后会在代码句法上的外层函数中的局部作用域,从内到外查找;之后查找当前的全局作用域,最后查找内置作用域;
- 在默认情况下,一个赋值会创建或改变当前作用域中的变量名;
注意,global声明会将变量映射至外层模块。
嵌套作用域举例
X = 99
def f1():
X = 88
def f2():
print(X)
f2()
f1()
工厂函数:闭包
一个简单的函数工厂
>>> def maker(N):
... def action(X):
... return X ** N
... return action
上面的代码定义了一个外层函数,用来简单地生成并返回一个嵌套的函数,却并不调用这个内嵌的函数。
如果调用外部函数:
>>> f = maker(2)
>>> f
<function maker.<locals>.action at 0x10bdf60e0>
我们得到的是生成的内嵌函数的一个引用。如果从外部调用这个函数将得到这个这个函数的返回值:
>>> f(3)
9
>>> f(4)
16
从上面的代码可以看出,在外层嵌套局部作用域内的N被作为执行的状态信息保留下来;
nonlocal语句
nonloca1语句在形式和作用上都和前面介绍过的global语句很像。 nonlocal和 global一样样,声明将要在外层作用域中修改的名称。和global的不同之处在于,nonlocal作用于外层函数的作用域中的一个名称,而不是所有def之外的全局模块作用域;而且在声明nonlocal名称的时候,它必须已经存在于该外层函数的作用域中,也就是说,它们只能现成存在于外层函数中,而不能由内嵌def中的第一次赋值来创建。
换句话说, nonlocal既允许对外层函数的作用域中名称的赋值,又限制该名称的作用域查找只能在外层def中进行。最终效果是为那些不想要或不需要带有属性、继承和多态行为的类的使用场景,提供了一种更加可靠和直接的可更改状态信息的实现。
nonlocal基础
nonloca1的引入井没有改变通用的名称引用作用域规则,它们仍然像以前一样工作,即遵从前面所描述的“LEGB°规则。non1oca1语句的主要作用是能让外层作用域中的名称被修改,面不仅仅是被引用。然而,在函数中使用g1ba1和 nonlocal语句都在某种程度上收紧甚至限制了查找规则。
最后
以上就是孝顺帆布鞋为你收集整理的第十七章 作用域python作用域基础global语句作用域和嵌套函数nonlocal语句的全部内容,希望文章能够帮你解决第十七章 作用域python作用域基础global语句作用域和嵌套函数nonlocal语句所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复