您的位置:首页 > 编程语言 > Python开发

python 变量作用域

2013-07-05 11:58 218 查看
http://hi.baidu.com/kanif/item/8c0947ece7cfaad4ea34c997

在python中,变量查找遵循LGB原则,即优先在局部作用域(local scope)中对变量进行查找,失败则在全局作用域(global scope)中进行查找,最后尝试再内建作用域(build-in scope)内查找,如果还是未找到的话,则抛出异常。后来由于闭包和嵌套函数的出现,作用域又增加了外部作用域,这样变量的查找作用域优先级变为:局部、外部、全局和内建。 作用域由def、class、lambda等语句产生,if、try、for等语句并不会产生新的作用域。我们看下面的一个例子:

def scope1_f1():

def f2():

print local_v

local_v = 'local'

f2()

print global_v

if __name__ == "__main__":

global_v = 'global'

scope1_f1()

在scope1_f1函数中我们并未对global_str进行赋值(即scope1_f1的局部作用域中并不存在变量lobal_str),但程序正常输出结果'local'和'global'。

一 局部作用域中不应对全局变量进行赋值

需要注意的是虽然我们可以在函数中对全局的变量进行访问,但一旦局部作用域中对全局变量进行了赋值操作,python解释器就不会从全局作用域中查找,而会抛出UnboundLocalError错误。该规则在由局部作用域向外部作用域查找时同样有效。

def scope1_f1():

print global_v

global_v = 'local'

if __name__ == "__main__":

global_v = 'global'

scope1_f1()

运行程序我们会得到:UnboundLocalError: local variable 'global_v' referenced before assignment

有资料将该规则描述为“局部作用域中全局变量应是只读”是不准确的。因为如果变量为list等类型,我们可以通过append这样的方法来修改全局变量,而不影响局部作用域对变量的访问。

def scope1_f1():

print global_v

global_v.add('local')

if __name__ == "__main__":

global_v = ['global']

scope1_f1()

二 局部作用域中使用 global实现对全局变量进行赋值

如果说确实需要对全局变量进行赋值的话,应在局部作用域中使用global来修饰变量。python2.6中global对变量的修饰可以在函数的任意地方进行,但如果global在赋值之后的话,解释器会提示SyntaxWarning 。

def scope1_f1():

global global_v

print global_v

global_v = 'local'

if __name__ == "__main__":

global_v = 'global'

scope1_f1()

三 继承类优先使用第一个基类中的变量

在没有对变量初始化的情况下,继承类会优先使用第一个基类中的变量。下面的例子会输出1而不是2。(说明:如果继承类为初始化函数,会优先调第一个基类的初始化函数,如果前面的基类都没有的话才会调后面基类的初始化函数,初始化函数对变量的修改不在本文讨论范围),

class sclass1():

a = 1

def run(self):

print self.a

class sclass2():

a = 2

def run(self):

print self.a

class dclass(sclass1, sclass2):

def run(self):

print self.a

if __name__ == "__main__":

a = dclass()

a.run()

四 全局作用域指的是本模块而不是程序

在变量查找时只会在本模块范围内进行变量的查找,即使使用from xxx import *也不会垮模块查找。在python中导入一个模块可以理解为是将另外一个模块各变量赋值给当前模块的同名变量, 对当前模块中变量的赋值不会影响到导入模块的变量。

main.py

from module2 import *

if __name__ == "__main__":

module2_v = 5

module2_v1 = 3

print_modul2()

modul2.py

module2_v1 = 'module2'

def print_modul2():

print module2_v1

print module2_v

在python中一切都是对象和变量,因此对函数、模块等的的查找同样遵循上面的规则。

最后还存在的一个问题是:使用global可以再局部作用域中对全局变量赋值,那么如何在局部作用域中对外部变量赋值呢? 目前除了使用list这样的引用传递外我还没发现其他解决方法,留在这里大家一起思考吧。

/article/4966486.html

Python是静态作用域语言,尽管它自身是一个动态语言。也就是说,在Python中变量的作用域是由它在源代码中的位置决定的,这与C有些相似,但是Python与C在作用域方面的差异还是非常明显的。

接下来会谈论Python的作用域规则,在这中间也会说明一下Python与C在作用域方面的不同。

在Python 2.0及之前的版本中,Python只支持3种作用域,即局部作用域,全局作用域,内置作用域;在Python 2.2中,Python正式引入了一种新的作用域 --- 嵌套作用域;在Python 2.1中,嵌套作用域可以作为一个选项被开启;嵌套作用域的引入,本质上为Python实现了对闭包的支持,关于闭包的知识,网上有很多解释,这里就不详细展开了。相应地,变量查找顺序由之前的LGB变成LEGB(L:Local,E:Enclosing,G:Global,B:Built-in)。

在Python中,并不是任何代码块都能引入新的作用域,这与C有很大的不同:

#include<stdio.h>
int main() {
if(2 > 0) {
int i = 0;
}
printf("i = %d", i);
return 0;
}


在这段代码中,if子句引入了一个局部作用域,变量i就存在于这个局部作用域中,但对外不可见,因此,接下来在printf函数中对变量i的引用会引发编译错误。

但是,在Python中却并非如此:

if True:
i = 0
print i


在这段代码中,if子句并没有引入一个局部作用域,变量i仍然处在全局作用域中,因此,变量i对于接下来的print语句是可见的。

实际上,在Python中,只有模块,类以及函数才会引入新的作用域,其它的代码块是不会引入新的作用域的。

在Python中,使用一个变量之前不必预先声明它,但是在真正使用它之前,它必须已经绑定到某个对象;而名字绑定将在当前作用域中引入新的变量,同时屏蔽外层作用域中的同名变量,不论这个名字绑定发生在当前作用域中的哪个位置。

def f():
print i
f()


运行结果将显示:NameError: global name 'i' is not defined。Python首先在函数f的本地作用域中查找变量i,查找失败,接着在全局作用域和内置作用域中查找变量i,仍然失败,最终抛出NameError异常。

i = 0
def f():
i = 8
print i
f()
print i


运行结果显示:8和0。i = 8是一个名字绑定操作,它在函数f的局部作用域中引入了新的变量i,屏蔽了全局变量i,因此f内部的print语句看到的是局部变量i,f外部的print语句看到的是全局变量i。

i = 0
def f():
print i
i = 0
f()


运行结果显示:UnboundLocalError: local variable 'i' referenced before assignment。在这个例子当中,函数f中的变量i是局部变量,但是在print语句使用它的时候,它还未被绑定到任何对象之上,所以抛出异常。

print i
i = 0


不论是以交互的方式运行,还是以脚本文件的方式运行,结果都显示:NameError: name 'i' is not defined。这里的输出结果又与上一个例子不同,这是因为它在顶级作用域(模块作用域)的缘故。对于模块代码而言,代码在执行之前,没有经过什么预处理,但是对于函数体而言,代码在运行之前已经经过了一个预处理,因此不论名字绑定发生在作用域的那个位置,它都能感知出来。Python虽然是一个静态作用域语言,但是名字查找确实动态发生的,因此直到运行的时候,才会发现名字方面的问题。

在Python中,名字绑定在所属作用域中引入新的变量,同时绑定到一个对象。名字绑定发生在以下几种情况之下:

1.参数声明:参数声明在函数的局部作用域中引入新的变量;

2.赋值操作:对一个变量进行初次赋值会在当前作用域中引入新的变量,后续赋值操作则会重新绑定该变量;

3.类和函数定义:类和函数定义将类名和函数名作为变量引入当前作用域,类体和函数体将形成另外一个作用域;

4.import语句:import语句在当前作用域中引入新的变量,一般是在全局作用域;

5.for语句:for语句在当前作用域中引入新的变量(循环变量);

6.except语句:except语句在当前作用域中引入新的变量(异常对象)。

在Python中,类定义所引入的作用域对于成员函数是不可见的,这与C++或者Java是很不同的,因此在Python中,成员函数想要引用类体定义的变量,必须通过self或者类名来引用它。

嵌套作用域的加入,会导致一些代码编译不过或者得到不同的运行结果,在这里Python解释器会帮助你识别这些可能引起问题的地方,给出警告。

locals函数返回所有的局部变量,但是不会返回嵌套作用域中的变量,实际上没有函数会返回嵌套作用域中的变量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: