一:变量作用域

变量可以是局部域或者全局域。定义在函数内的变量有局部作用域,在一个模块中最高级别的变量有全局作用域。

全局变量的一个特征是除非被删除掉,否则它们的存活到脚本运行结束,且对于所有的函数,他们的值都是可以被访问的。然而局部变量,仅仅只依赖于定义它们的函数现阶段是否处于活动。

在Python中,变量查找遵循LGB原则,即优先在局部作用域(local scope)中对变量进行查找,失败则在全局作用域(globalscope)中进行查找,最后尝试再内建作用域(build-inscope)内查找,如果还是未找到的话,则抛出NameError异常。所以,局部变量可以“隐藏“或者覆盖一个全局变量。

后来由于闭包和嵌套函数的出现,作用域又增加了外部作用域,这样变量的查找作用域优先级变为:局部、外部、全局和内建。 作用域由def、class、lambda等语句产生,if、try、for等语句并不会产生新的作用域。所以下面的函数:

def foo():
if True:
a = 2
print a

其中的变量a,还是存在于局部作用域,而不是由if语句引入的新的作用域。所以,它对于print语句还是可见的。这与C语言中的情况是不同的。

需要注意下面的情况:

def  scope1_f1():
print global_v
global_v = 'local' if__name__ == "__main__":
global_v = 'global'
scope1_f1()  

运行程序,会得到:UnboundLocalError: localvariable 'global_v' referenced before assignment

这是因为,当在函数内部为一个变量赋值时,该变量就默认为局部变量。所以,上面的scope1_f1函数中,解释器就会认为global_v是一个局部变量,因此赋值引起了异常。下面的情况也类似:

>>>gv = 4
>>>def bar():
... gv += 2...
>>>gv
4 >>>bar()
Traceback(most recent call last):
File "<stdin>", line 1, in<module>
File "<stdin>", line 2, inbar
UnboundLocalError:local variable 'gv' referenced before assignment

在函数bar中,即使换成gv = gv + 2也是一样抛出异常。

局部变量覆盖全局变量的例子:

>>>def foo():
... lv = 2
... gv = 3
... print 'lv is %d, gv is %d'%(lv, gv)
... >>>gv = 4
>>>foo()
lv is2, gv is 3
>>>gv

在函数内部,明确要使用一个已命名的全局变量的时候(避免其被局部变量覆盖),必须使用global 语句。global的语法如下:global var1[, var2[, ... varN]]]

在Python2.1之前的版本中,最多就两个作用域:一个函数的局部作用域和全局作用域。虽然存在多个函数的嵌套,但你不能访问超过两个作用域。

def  foo():
m = 3
def bar():
n = 4
print m + n
print m
bar()

虽然这代码在今天能完美的运行,但在python2.1 之前执行它将会产生错误。

>>>foo()
Traceback(innermost last):

NameError:m 

Python 的lambda 匿名函数遵循和标准函数一样的作用域规则。一个lambda 表达式定义了新的作用域,就像函数定义,所以这个作用域除了局部lambda/函数,对于程序其他部分,该作用域都是不能对进行访问的。

二:闭包

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。定义在外部函数内的但由内部函数引用或者使用的变量被称为自由变量。闭包在函数式编程中是一个重要的概念。

闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。

闭包和函数调用没多少相关,而是关于使用定义在其他作用域的变量。简单的闭包的例子:

def  line_conf():
b = 15
def line(x):
return 2*x+b
return line # return a functionobject my_line= line_conf()
print(my_line(5)) 

line函数中引用了高层级的变量b,但b信息存在于line的定义之外。称b为line的环境变量。事实上,line作为line_conf的返回值时,line中已经包括b的取值(尽管b并不隶属于line)。上面的代码将打印25.

一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。在Python中,所谓的闭包是一个包含有环境变量取值的函数对象。

另一个闭包的例子:

def  counter(start_at=0):
count =[start_at]
def incr():
count[0] += 1
return count[0]
return incr 

创建了一个闭包incr,它现在携带了整个counter()作用域。incr()增加了正在运行的count,然后返回它:

>>>count =counter(5)

>>>print count()
6 >>>print count()
7 >>>count2 =counter(100)
>>>print count2()
101 >>>print count()
8

外部函数退出之后,它的命名空间也就不存在了。但在闭包中,内部函数依然维持了外部函数中自由变量的引用—单元。内部函数不能修改单元对象的值(但是可以引用)。若尝试修改,则解释器会认为它是局部变量。这类似于全局变量和局部变量的关系。如果在函数内部修改全局变量,必须加上global声明,但是对于自由变量,尚没有类似的机制。所以,只能使用列表。(python3中引入了关键字:nonlocal)

所以,在上面的例子中,使用了列表,而不是直接使用该变量本身。如果改为:

def  counter2(start_at=0):
count = start_at
def incr():
count += 1
return count
return incr 

则在调用

count= counter(5)
count()

会引发异常,这与上一节中修改全局变量时的异常本质一样:

count += 1

UnboundLocalError: local variable 'count'referenced before assignment

可以通过函数的func_closure属性来追踪自由变量:

output = '<int %r id=%#0x val=%d>'

w = x= y = z = 1

def  f1():
x = y = z = 2 def f2():
y = z = 3 def f3():
z = 4
print output % ('w', id(w), w)
print output % ('x', id(x), x)
print output % ('y', id(y), y)
print output % ('z', id(z), z) clo = f3.func_closure
if clo:
print "f3 closurevars:", [str(c) for c in clo]
else:
print "no f3 closurevars"
f3() clo = f2.func_closure
if clo:
print "f2 closurevars:", [str(c) for c in clo]
else:
print "no f2 closurevars"
f2() clo = f1.func_closure
if clo:
print "f1 closure vars:", [str(c)for c in clo]
else:
print "no f1 closure vars" f1() 

运行结果如下:

no f1 closure vars

f2  closure vars: ['<cell at 0x0164BD10: int object at 0x0157ACC4>']

f3  closure vars: ['<cell at 0x0164BD10: int object at 0x0157ACC4>', '<cell at 0x0164BDB0: int object at0x0157ACB8>']

<int'w' id=0x157acd0 val=1>
<int'x' id=0x157acc4 val=2>
<int'y' id=0x157acb8 val=3>
<int'z' id=0x157acac val=4>

f1创建局部变量x,y 和z。它的内部函数f2创建了局部变量y和z。f2的内部函数f3创建了局部变量z。

f3中访问了w,x,y,z。其中w为全局变量,z为局部变量,所以,x和y为f3访问的自由变量,记录在f3.func_closure中。

f2虽然没有直接访问任何变量,但因为其内部函数f3访问了w, x, y, z。所以,也认为f2访问了这些变量。其中,w为全局变量,y、z为局部变量。所以,只有x为f2访问的自由变量,记录在f2.func_closure中。

参考:

https://docs.python.org/2/faq/programming.html?highlight=unboundlocalerror#why-am-i-getting-an-unboundlocalerror-when-the-variable-has-a-value

http://www.cnblogs.com/vamei/archive/2012/12/15/2772451.html

Python基础:11变量作用域和闭包的更多相关文章

  1. 十一. Python基础(11)—补充: 作用域 & 装饰器

    十一. Python基础(11)-补充: 作用域 & 装饰器 1 ● Python的作用域补遗 在C/C++等语言中, if语句等控制结构(control structure)会产生新的作用域 ...

  2. Python入门笔记(22):Python函数(5):变量作用域与闭包

    一.全局变量与局部变量 一个模块中,最高级别的变量有全局作用域. 全局变量一个特征就是:除非被删除,否则他们存活到脚本运行结束,且对于所有的函数都可访问. 当搜索一个标识符(也称变量.名字等),Pyt ...

  3. Python学习(21):Python函数(5):变量作用域与闭包

    转自 http://www.cnblogs.com/BeginMan/p/3179040.html 一.全局变量与局部变量 一个模块中,最高级别的变量有全局作用域. 全局变量一个特征就是:除非被删除, ...

  4. Python基础之变量作用域

    一.分类: 二.变量名的查找规则: 三.局部变量: 四.全局变量: 五.global语句: 六.nonlocal语句: 七.基础代码: # 全局变量:当前.py文件内部都可访问 g01 = 100 d ...

  5. python 变量作用域、闭包

    先看一个问题: 下面代码输出的结果是0,换句话说,这个fucn2虽然已经用global声明了variable1,但还是没有改变变量的值 def func1(): variable1=0 def fun ...

  6. 九. Python基础(9)--命名空间, 作用域

    九. Python基础(9)--命名空间, 作用域 1 ● !a 与 not a 注意, C/C++可以用if !a表示if a == 0, 但是Python中只能用if not a来表示同样的意义. ...

  7. python 函数及变量作用域及装饰器decorator @详解

    一.函数及变量的作用   在python程序中,函数都会创建一个新的作用域,又称为命名空间,当函数遇到变量时,Python就会到该函数的命名空间来寻找变量,因为Python一切都是对象,而在命名空间中 ...

  8. 『Python基础-11』集合 (set)

    # 『Python基础-11』集合 (set) 目录: 集合的基本知识 集合的创建 访问集合里的值 向集合set增加元素 移除集合中的元素 集合set的运算 1. 集合的基本知识 集合(set)是一个 ...

  9. Python 名称空间与作用域、闭包与装饰器

    Python 的名称 Python 的名称(Name)是对象的一个标识(Identifier).我们知道,在 Python 里面一切皆对象,名称就是用来引用对象的.说得有点玄乎,我们以例子说明. 例如 ...

随机推荐

  1. JavaScript实现继承的方式和各自的优缺点

    ECMAscript只支持实现继承,主要是依靠原型链来实现的. JavaScript实现继承的方式: 类式继承 构造函数继承 组合继承 寄生组合式继承 1.类式继承 //类式继承 //声明父类 fun ...

  2. 如何在linux中部署mongodb并设置连接认证

    在windows上给mongodb设置连接认证权限:mongodb默认是不认证的,默认没有账号,现在就讲讲怎么设置账户和密码 1.首先进入C:\mongodb\bin下面双击运行mongo.exe启动 ...

  3. ztree树节点重叠问题

    使用zTree时,由于同时使用了bootstrap插件,导致样式起了冲突,生成的树都挤在一起了, 最后的解决办法是设置zTreeStyle.css文件的.ztree li ul{}属性,在里面加入he ...

  4. 【P1203】 【USACO1.1】坏掉的项链Broken Necklace

    P1203 [USACO1.1]坏掉的项链Broken Necklace 题目描述 你有一条由N个红色的,白色的,或蓝色的珠子组成的项链(3<=N<=350),珠子是随意安排的. 这里是 ...

  5. (转)Sql server中 如何用sql语句创建视图

    1.视图的作用 视图的作用: 第一点:使用视图,可以定制用户数据,聚焦特定的数据. 解释:     在实际过程中,公司有不同角色的工作人员,我们以销售公司为例的话,     采购人员,可以需要一些与其 ...

  6. 浅谈mybatis中#{}和${}的区别

    #{}:表示占位符,如果获取简单类型,#{}中可以使用value或其它名称.有效防止sql注入.使用#{}设置参数无需考虑参数的类型. 如果使用#{}比较日期字段,select* from table ...

  7. leetcode 350 easy

    350. Intersection of Two Arrays II class Solution { public: vector<int> intersect(vector<in ...

  8. 【笔记】LR录制方式和常用函数

    本文为本人复习LR时,笔记整理.以备后续查阅. 注意:录制脚本时,选择不同的协议下录制时设置选项也是不相同的,我们这里介绍的是基于协议web(http/html)录制选项设置. 对于web(http/ ...

  9. ICP算法(迭代最近点)

    参考博客:http://www.cnblogs.com/21207-iHome/p/6034462.html 最近在做点云匹配,需要用c++实现ICP算法,下面是简单理解,期待高手指正. ICP算法能 ...

  10. PHP加密解密方法

    加密解密方法 //字符串解密加密 function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { $ckey_l ...