Python一一作用域

Python作用域基础

Python中作用域这个术语指的就是命名空间,代码中变量名被赋值的位置决定了这个变量名能被访问到的范围。

变量可以在3个不同的地方分配,分别对应3种不同的作用域:

(1).如果一个变量在def内赋值,它被定位在这个函数之内。

(2).如果一个变量在一个嵌套的def中赋值,对于嵌套的函数来说,它是非本地的。

(3).如果在def之外赋值,它就是整个文件全局的。

作用域法则

函数定义了本地作用域,而模块定义的是全局作用域。这两个作用域有如下关系:

(1).内嵌的模块是全局作用域。每个模块都是一个全局作用域

(2).全局作用域的作用范围仅限于单个文件。这里的全局指的是在一个文件的顶层的变量名仅对于这个文件内部的代码而言是全局的。

(3).每次都函数的调用都创建了一个新的本地作用域。

(4).赋值的变量名除非声明为全局变量或非本地变量,否则均为本地变量。在默认情况下,所有函数定义内部的变量名是唯一本地作用域内的。如果需要给一个下函数内部却位于模块文件顶层的变量名赋值,需要在函数内部通过global语句声明。如果需要给位于一个嵌套的def中的名称赋值,从Python3.0开始通过在一条nonlocal语句中声明它来做到。

(5).所有其他的变量名都可以归纳为本地、全局或者内置的。

变量名解析:LEGB原则

作用域实例

X,func为全局变量名,Y,Z为本地变量名

内置作用域

内置作用域仅仅是一个名为__builtin__的内置模块,但是必须要import__builtin__之后才能使用内置作用域,因为变量名builtin本身并没有预先内置。

有两种方法引用一个内置函数:通过LEBG法则和手动导入__builtin__模块

在本地作用域的变量名可能会覆盖在全局作用域和内置作用域的有着相同变量名的变量,而全局变量名有可能覆盖内置的变量名。

global语句

它告诉Python函数打算生成一个或多个全局变量名。也就是说,存在于整个模块内部作用域(命名空间)的变量名。

(1).全局变量是位于模块文件内部的顶层的变量名。

(2).全局变量如果是在函数内被赋值的话,必须经过声明。

(3).全局变量名在函数的内部不经过声明也可以被引用。

global允许我们修改一个模块文件的顶层的一个def之外的名称。nonlocal语句应用于嵌套的def的本地作用域内的名称,而不是嵌套的模块中的名称。

y,z是全局变量,因为它们不是在函数内赋值的;x是全局变量,因为它通过global语句使自己明确地映射到了模块的作用域。如果不使用global语句的话,x将会由于赋值而被认为是本地变量。

注意:x在函数运行前可能并不存在。

最小化全局变量

慎用全局变量,一个程序可以委任一个单个的模块文件去定义所有的全局变量。

最小化文件间的修改

考虑下面两个模块文件

尽管这样的跨文件变量在Python中总是可能修改的,但它们通常比我们想要的更微妙。再者,这会让两个文件有过于强的相关性:因为它们都与变量X的值相关,如果没有其中一个文件的话很难理解或重用另一个文件。这种隐含的跨文件依赖性,在最好的情况下导致代码不灵活,最坏的情况下会引发bug。

在文件间进行通信最好的办法就是通过调用函数,传递参数,然后得到其返回值:

这样做的好处就是程序员知道这是一个接入点,并且知道这将改变变量X。这样就能让我们最小化文件间变量的改变。

其他访问全局变量的方法

由于全局变量构成了一个被导入的对象的属性,我们能够通过使用导入嵌入的模块并对其属性进行赋值来仿造出一个global语句,通过变量名然后通过索引sys.modules导入了嵌套的模块,如下:

作用域和嵌套函数

我们命名为f2的函数的调用动作的运行是在f1运行后发生的。f2记住了在f1中嵌套作用域中的x,尽管f1已经不处于激活状态。

工厂函数

闭合/工厂函数一一 一个能够记住嵌套作用域的变量值的函数,尽管那个作用域或许已经不存在了。(类是最适合用作记忆状态的)

工厂函数有时用于需要及时生成事件处理、实时对不同情况进行反馈的程序中(例如,用户的输入是无法进行预测的)

maker函数内部的名为action的函数,这一部分最不平常的就是,内嵌的函数记住了整数2,即maker函数内部的变量N的值,尽管在调用执行f时maker已经返回了值并退出。实际上,在本地作用域内的N被作为执行的状态信息保留了下来,我们返回其参数的平方运算。

使用默认参数来保留嵌套作用域的状态

嵌套作用域和lambda

nonlocal基础

Python3.0引入了一条新的nonlocal语句,它只在一个函数内有意义:

def func():
    nonlocal name1, name2, ...

nonlocal应用

下面的代码,tester构建并返回函数nested以便随后调用,nested中的state引用使用超常规的作用域查找规则来映射tester的本地作用域:

默认情况下,不允许修改嵌套的def作用域中的名称:

使用nonlocal进行修改

边界情况

和global不同,当执行一条nonlocal语句时,nonlocal名称必须已经在一个嵌套的def作用域中赋值过,否则将会得到一个错误一一不能通过在嵌套的作用域中赋给它们一个新值来创建它们:

其次,nonlocal限制作用域查找仅为嵌套的def,nonlocal不会在嵌套的模块的全局作用域或所有def之外的内置作用域中查找,即便已经有了这些作用域:

上述nonlocal语句只适用于Python3.X中,如果使用的是Python2.X,则应该有一些替代方法,如下:

与全局共享状态

方法就是直接把状态移出全局作用域(嵌套的模块):

缺点:它需要在两个函数中都有global声明,并且倾向于引起全局作用域的名称冲突,另外,如果再次调用tester,将会重新设置模块的状态变量,以至于前面的调用将会看到自己的状态被覆盖,如上图红色标记。

使用类的状态(预览)

针对可改变信息的另一种较早的方法是使用带有属性的类,从而让状态信息的访问比隐式的范围查找规则更明确。作为一个额外的优点,一个类的每个实例都得到状态信息的一个新副本,作为Python对象模型的一个天然的副产品。

我们也可以使用运算符重载让类看上去像是一个可调用函数。__call__获取了一个实例上的直接调用,因此,我们不需要调用一个指定的方法:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值