python变量作用域

昨天学习了python的静态嵌套域,但是网上差了很多资料看了很多博客,也没有弄清除,最后还是得看PEP227.在PEP227中原文是这样说的:

The Python 2.0 definition specifies exactly three namespaces to check for each name -- 
the local namespace, the global namespace, and the builtin namespace. According to this definition,
 if a function A is defined within a function B, the names bound in B
are not visible in A.
The proposal changes the rules so that names bound in B are visible in A (unless A contains a name binding that hides the binding in B).

其实也就是说当函数A定义在函数B中时,即嵌套定义时,函数B中的变量在函数A中是不可见的,
此时python称函数A中的B的变量为自由变量,函数A为闭包,而在python2.1以后B中的变量在A中是可见的了,我写了个程序试了下,发现确实是这样:

#!/user/bin/env python
def foo():
m=3
def bar():
print m
bar()
print m
foo()
结果bar()中的print 语句一样可以打出m的值,但是如果在bar()中声明了一个局部变量m,这是bar()中的m就会屏蔽foo()中的m,这一点与C/C++是一样的。
我又试着写了这样一个程序:
#!/user/bin/env python
def foo():
m=3
def bar():
      z=4
print m
bar()
print z
foo()
这个程序实在bar()中申明了一个变量z,然后在foo中打印z,结果发现此时python编译器会报错
此外,对于静态嵌套域还有其他的规则,在这里引用一下双宇的博文:

如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).

闭包在函数式编程中是一个重要的概念。语法上看比较简单,但是用处却是相当广泛的。
在Python 2.1版本以前,只有全局域和局部作用域,而在2.1以后的版本中我们可以使用静态嵌套域,

如像下面这样的嵌套函数中,在以前,内部函数是不能访问外部函数作用域中的变量的。
def foo():
    m = 3
    def bar():
        n = 4
        print m + n
    print m
 bar()
而 在现在的版本中可以完美运行,而bar()中的 m 就是一个既不属于全局域又不属于局部域的闭包变量,

它存活在一个函数的名称空间和作用域---嵌套作用域。而在闭包中对嵌套作用域中的访问规则与上面讨论 的Global是一样的。

即在对闭包变量 m 的重新声明之前引用 m 都会引发异常
def foo():
    m = 3
    def bar():
        print m  #UnboundLocalError
        m=4
        print m
 bar()
UnboundLocalError: local variable 'm' referenced before assignment
为 什么会这样呢?其实是因为m的类型有关,我们知道Pyhton中的基本数据类型分为可变和不可变,

对于不可变类型的赋值,其实是重新定义一个新的变量对 象,并深拷贝原对象到新对象,参考str类型说明。

如果将上面的 m 声明成可变类型list,那就不会产生这个异常了。
 def foo():
      m = [3]
      def bar():
          print m[0]
          m[0]=4
         print m[0]
      bar()
关于可变类型与不可变类型的说明,这里就不展开说了,大家可以看API Document

下面举一个闭包的实际例子:
def hellocounter (name):
    count=[0]
    def counter():
        count[0]+=1
        print 'Hello,',name,',',str(count[0])+' access!'
    return counter

hello = hellocounter('ysisl')
hello()
hello()
hello()

Console output:
 Hello, ysisl , 1 access!
 Hello, ysisl , 2 access!
 Hello, ysisl , 3 access!
这 个例子中,hellocounter(name)返回了一个内部函数counter的引用,就像C++中指向函数的指针一样,

我们把这个引用用一个变量 hello来保存,那么这个变量就代表了这个counter函数,为什么对hello()的反复调用能保持闭包变量count的自增,

而不是释放后再重新 分配?因为count不是counter的局部变量,它是一个在hellocounter()范围内游离于counter之外的变量,

只有当 hellocounter(name)被调用时,count才被重新分配新的内存地址。

双宇原文地址:http://www.cnblogs.com/ysisl/archive/2009/08/17/1548075.html




posted @ 2011-09-14 11:04  wait123  阅读(4868)  评论(0编辑  收藏  举报