zoukankan      html  css  js  c++  java
  • python 变量作用域、闭包

    先看一个问题:

      下面代码输出的结果是0,换句话说,这个fucn2虽然已经用global声明了variable1,但还是没有改变变量的值

    def func1():
        variable1=0
        def func2():
            global variable1
            variable1=2
        func2()
        print(variable1)
    if __name__=="__main__":
        func1()
    
    #下面这段代码也是输出0
    variable1=0
    def func1():
        variable1=0
        def func2():
            global variable1
            variable1=2
        func2()
        print(variable1)
    if __name__=="__main__":
        func1()

      下面代码的输出结果是2,这个global起作用了

    variable1=0
    def func1():
        # variable1=0
        def func2():
            global variable1
            variable1=2
        func2()
        print(variable1)
    if __name__=="__main__":
        func1()
    

      

    变量作用域涉及到的知识:

    参考链接:https://www.jianshu.com/p/3bb277c2935c

        python中变量作用域的分类:

      python中的变量作用域一共有四种

    • 局部作用域(L:Local)

    局部变量是指在函数内部定义并使用的变量,它只在函数内部有效。离开函数之后就不能再访问局部变量了,否则解释器会抛出 NameError 错误。

    • 闭包函数外的函数中(E:Enclosing)

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

    • 全局作用域(G:Global)

    和局部变量相对应,全局变量指的是能作用于函数内外的变量,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。

    定义的方式有两种:在函数体外定义的变量,一定是全局变量;在函数体内定义全局变量。即使用 global 关键字对变量进行修饰后,该变量就会变为全局变量。

    • 内建作用域(B:Built-in)

    变量作用域中的一些规则

      在不同的变量作用域中查找变量的顺序

      以 L --> E --> G -->B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。

      所以python中对变量的命名没有关键字冲突,但是会出现这样的情况,在重复的变量的作用域中

    >>> str(2)
    '2'
    >>> str=5
    >>> str
    5
    >>> str(2)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'int' object is not callable
    >>>
    

      

      另外这样的查找顺序还可能会带来另一个问题:全局变量和局部变量的遮蔽现象(同名的时候喽)

      全局变量默认可以在所有函数内被访问,但如果在函数中定义了与全局变量同名的变量,此时就会发生局部变量遮蔽(hide)全局变量的情形

    var = 1
    def fun():
        print var
        var = 200
    print fun()
    
    #file2.py
    var = 1
    def fun():
        var = var + 1
        return var
    print fun()
    # 这两个函数都会报错UnboundLocalError: local variable 'var' referenced before assignment
    

      因为在函数的内部,解释器探测到var被重新赋值了,所以var成为了局部变量,但是在没有被赋值之前就想使用var,便会出现这个错误。解决的方法是在函数内部添加 globals var 但运行函数后全局的var也会被修改。

     

      python能够改变变量作用域的代码段是def、class、lamda.其他如: if/elif/else/ try/except for/while并不能改变其作用域。定义在他们之内的变量,外部还是可以访问。

    >>> if True:
    ...     a = 'I am A'
    ... 
    >>> a
    'I am A'
    # 定义在if语言中的变量a,外部还是可以访问的。
    # 但是需要注意如果if被 def/class/lambda 包裹,在内部赋值,就变成了此 函数/类/lambda 的局部作用域。
    

      在 def/class/lambda内进行赋值,就变成了其局部的作用域,局部作用域会覆盖全局作用域,但不会影响全局作用域。

     

    闭包

      

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

      在最开始的问题中,访问 func1中的variable1的func2就是一个闭包,Python3有个关键字nonlocal可以解决这个问题,但在Python2中还是不要尝试修改闭包中的变量。

    def func1():
        variable1=0
        def func2():
            nonlocal variable1#这样就好了
            variable1=2
        func2()
        print(variable1)
    if __name__=="__main__":
        func1()
    

      关于闭包中还有一个坑:

    from functools import wraps
    
    def wrapper(log):
        def external(F):
            @wraps(F)
            def internal(**kw):
                if False:
                    log = 'modified'
                print log
            return internal
        return external
    
    @wrapper('first')
    def abc():
        pass
    
    print abc()
    #也会出现nameerror:引用在定义之前

      原因是解释器探测到了 if False 中的重新赋值,根据变量的搜索规则,所以不会去闭包的外部函数(Enclosing)中找变量,但 if Flase 不成立没有执行(所以log赋值就没有执行即没有定义 ),所以便会出现此错误。除非你还需要else: log='var' 或者 if True 但这样添加逻辑语句就没了意义(为什么没有意义呢?),所以尽量不要修改闭包中的变量

      

    获取指定作用域范围中的变量

      参考链接:http://c.biancheng.net/view/2259.html

      不管是在函数的局部范围内还是在全局范围内,都可能存在多个变量,每个变量“持有”该变量的值。从这个角度来看,不管是局部范围还是全局范围,这些变量和它们的值就像一个“看不见”的字典,其中变量名就是字典的 key,变量值就是字典的 value。实际上,Python 提供了如下三个工具函数来获取指定范围内的“变量字典”:

    1. globals():该函数返回全局范围内所有变量组成的“变量字典”。
    2. locals():该函数返回当前局部范围内所有变量组成的“变量字典”。
    3. vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如果不传入object 参数,vars() 和 locals() 的作用完全相同。

      

      globals() 和 locals() 看似完全不同,但它们实际上也是有联系的,关于这两个函数的区别和联系大致有以下两点:

    • locals() 总是获取当前局部范围内所有变量组成的“变量字典”,因此,如果在全局范围内(在函数之外)调用 locals() 函数,同样会获取全局范围内所有变量组成的“变量字典”;而 globals() 无论在哪里执行,总是获取全局范围内所有变量组成的“变量字典”。
    • 一般来说,使用 locals() 和 globals() 获取的“变量字典”只应该被访问,不应该被修改。但实际上,不管是使用 globals() 还是使用 locals() 获取的全局范围内的“变量字典”,都可以被修改,而这种修改会真正改变全局变量本身:但通过 locals() 获取的局部范围内的“变量字典”,即使对它修改也不会影响局部变量。
  • 相关阅读:
    this.get_element .style为空或不是对象
    在server2008R2的IIS7中调试asp程序遇到的错误
    Server.Transfer 在两个窗体之间传递参数用法
    页面事务处理 ContextUtil.SetComplete(); 没有 MTS 对象上下文
    VS2008切换设计视图卡死 停止响应
    javascript对象小结
    [JavaScript] 最低公用标准的浏览器文档对象层次
    Linq 此提供程序只支持对返回实体或投影(包含所有标识列)的有序查询使用 问题的解决
    [Design] Factory Pattern
    [Database] SqlServer: Linq to Sql THE DATACONTEXT Change Processing
  • 原文地址:https://www.cnblogs.com/Gaoqiking/p/11262648.html
Copyright © 2011-2022 走看看