美文网首页
python学习笔记 - local, global and f

python学习笔记 - local, global and f

作者: 黄土高坡 | 来源:发表于2015-02-07 23:28 被阅读4405次

    理解 global and local variable

    现在我想在子代码块中对global variable重新赋值,实现代码如下:

    <a id="code-snippet-1">code snippet 1</a>

    >>>
    >>> var = 'outer val'
    >>> def func1():
    ...     var = 'inner val'
    ...     print "Inside func1(), val: ", var
    ...     print "locals(): ", locals()
    ...     print "globals(): ", globals()
    ...
    >>> func1()
    Inside func1(), val:  inner val
    locals():  {'var': 'inner val'}
    globals():  {'func1': <function func1 at 0x10a20b938>, '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'var': 'outer val', '__name__': '__main__', '__doc__': None}
    >>>
    >>> print "var: ", var
    var:  outer val
    >>>
    

    但结果并非如我所愿。在func1内部,var的值为"inner val",离开了函数,其值又变成了"outer val",为什么?看看输出的本地和全局变量列表答案就明显了,var既出现在本地变量列表,又出现在全局变量列表,但绑定的值不一样,因为他们本就是两个不同的变量。也就是说,当你想在子代码块中对某个全局变量重新赋值时,实际上你只是定义了一个同名的本地变量,这并不会改变全局变量的值。

    如何修改全局变量的值?需要在函数中使用global statement。先看一例:

    <a id="code-snippet-2">code snippet 2</a>

    >>> def func2():
    ...     global var2
    ...     var2 = 'who am i'
    ...     print "locals(): ", locals()
    ...     print "globals(): ", globals()
    ...
    >>> func2()
    locals():  {}
    globals():  {'func2': <function func2 at 0x10a20bd70>, 'func1': <function func1 at 0x10a20b938>, 'var2': 'who am i', '__builtins__': <module '__builtin__' (built-in)>, '__package__': None, '__name__': '__main__', '__doc__': None}
    >>>
    >>> print var2
    who am i
    >>>
    

    看看输出结果,发现竟然可以在函数代码块中定义一个全局变量,太神奇了!因为全局变量的作用域是整个模块,所以离开了定义函数,依然可以读取它。

    有了这个做铺垫,再来尝试修改全局变量的值:

    <a id="code-snippet-3">code snippet 3</a>

    >>> global_var = 'outer'
    >>> def func3():
    ...     global global_var
    ...     global_var = 'inner'
    ...     print "locals(): ", locals()
    ...     print "globals(): ", globals()
    ...
    >>> func3()
    locals():  {}
    globals():  {'func3': <function func3 at 0x109dce938>, '__builtins__': <module '__builtin__' (built-in)>, 'global_var': 'inner', '__package__': None, '__name__': '__main__', '__doc__': None}
    >>>
    >>> print global_var
    inner
    >>>
    

    妥妥的了。做个总结吧,要在函数代码块中修改全局变量的值,就使用global statement声明一个同名的全局变量,然后就可以修改其值了;如果事先不存在同名全局变量,此语句就会定义一个,即使离开了当前函数也可以使用它。

    理解 global and free variable

    来自于python官方文档 Execution Model 的解释:

    When a name is used in a code block, it is resolved using the nearest enclosing scope. The set of all such scopes visible to a code block is called the block’s environment.

    If a name is bound in a block, it is a local variable of that block. If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.) If a variable is used in a code block but not defined there, it is a free variable.

    这段文档确实应该多读多想。全局变量不用多说,一看就懂。不过这句话有点让人费解,“The variables of the module code block are local and global”,我是这样理解的,对于模块代码块来说,模块级的变量就是它的本地变量,但对于模块中其他更小的代码块来说,这些变量就是全局的。那再进一步,什么是模块级的变量,是指那些在模块内部,但在其所有子代码块之外定义的变量吗?用代码验证哈:

    <a id="code-snippet-4">code snippet 4</a>

    >>>
    >>> global_var = 'global_val'
    >>> def showval():
    ...     local_var = 'local_val'
    ...     print "before defining inner func, showval.locals(): ", locals()
    ...     def innerFunc():
    ...         print "Inside innerFunc, local_var: ", local_var
    ...         print "innerFunc.locals:", locals()
    ...     print "after defining inner func, showval.locals(): ", locals()
    ...     print "showval.globals():", globals()
    ...     return innerFunc
    ...
    >>> a_func = showval()
    before defining inner func, showval.locals():  {'local_var': 'local_val'}
    after defining inner func, showval.locals():  {'innerFunc': <function innerFunc at 0x109caed70>, 'local_var': 'local_val'}
    showval.globals(): {'__builtins__': <module '__builtin__' (built-in)>, 'global_var': 'global_val', '__package__': None, '__name__': '__main__', '__doc__': None, 'showval': <function showval at 0x109cae938>}
    >>>
    >>> a_func
    <function innerFunc at 0x109caed70>
    >>> a_func()
    Inside innerFunc, local_var:  local_val
    innerFunc.locals: {'local_var': 'local_val'}
    >>>
    

    基于自己的理解来分析哈输出结果:变量local_var、函数innerFunc是在函数代码块showval中定义的,所以它们是该代码块的局部变量。首次输出globals时,变量global_var、函数showval在所有子代码块之外定义,所以它们是当前模块的全局变量。再次输出globals时,多了一个全局变量globals。

    注意看,全局变量中还有:__builtins__、__package__、__name__、__doc__。这些变量的含义可参考 python模块的内置方法

    现在来看自由变量,local_var被innerFunc函数使用,但是并未在其中定义,所以对于innerFunc代码块来说,它就是自由变量。问题来了,自由变量为什么出现在
    innerFunc.locals()的输出结果中?这就需要看看locals()API文档了:

    Update and return a dictionary representing the current local symbol table. Free variables are returned by locals() when it is called in function blocks, but not in class blocks.

    好像我已经把自由变量搞得很清楚了,但是真的吗?看看下面的代码:

    <a id="code-snippet-5">code snippet 5</a>

    >>>
    >>> global_var = 'foo'
    >>> def ex1():
    ...     local_var = 'bar'
    ...     print "locals(): ", locals()
    ...     print global_var
    ...     print local_var
    ...     global_var = 'foo2'
    ...
    >>> ex1()
    locals():  {'local_var': 'bar'}
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 4, in ex1
    UnboundLocalError: local variable 'global_var' referenced before assignment
    >>>
    >>> def ex2():
    ...     local_var = 'bar'
    ...     print "locals(): ", locals()
    ...     print "globals(): ", globals()
    ...     print global_var
    ...     print local_var
    ...
    >>> ex2()
    locals():  {'local_var': 'bar'}
    globals():  {'__builtins__': <module '__builtin__' (built-in)>, 'global_var': 'foo', '__package__': None, 'ex2': <function ex2 at 0x10b91aed8>, 'ex1': <function ex1 at 0x10b91aaa0>, '__name__': '__main__', '__doc__': None}
    foo
    bar
    >>>
    

    首先分析哈输出结果。执行ex1()函数失败,原因:UnboundLocalError: local variable 'global_var' referenced before assignment,意思是global_var这个本地变量还未赋值就被引用了。等等,global_var怎么是本地变量了?与ex2()函数做个对比,发现因为有这行代码global_var = 'foo2',解释器就认为global_var是一个本地变量,而不是全局变量。
    那最外面定义的global_var到底是本地还是全局变量?看看第二部分的输出,可以很确定地知道最外面定义的global_var是一个全局变量。

    简单点说,就是这里存在两个同名的变量,一个是最外面定义的全局变量global_var,另一个是函数ex1()中定义的本地变量global_var。

    再等等,矛盾出现了。按照之前对自由变量的理解,在一个代码块中被使用,但是并未在那儿定义,外部定义的global_var在函数块ex1()中被使用,完全符合,它为什么不是一个自由变量?还是我们对自由变量的理解有问题?

    code snippet 4中确定是自由变量的local_var比较,发现local_var是一个函数代码块的本地变量,而code snippet 5中的global_var是一个模块代码块的本地变量。当local_var被定义它的函数内的嵌套函数使用时,它就变成了一个自由变量,此时函数的嵌套就形成了闭包(closure),可见自由变量的应用场景就是闭包。
    而global_var对模块本身来说它是一个本地变量,但在模块中的其他代码块中也可以使用它,所以它同时又是一个全局变量。

    到这儿就可以对自由变量的理解做个总结了:如果一个变量在函数代码块中定义,但在其他代码块中被使用,例如嵌套在外部函数中的闭包函数,那么它就是自由变量。注意,给变量赋值不算是使用该变量,使用指的是读取变量值,具体区别后面再详述。

    理解 free and local variable

    修改全局变量的值需要使用global,那修改自由变量的值又会如何?先用自然思维方式试试:

    <a id="code-snippet-6">code snippet 6</a>

    >>>
    >>> def outerfunc():
    ...     var = 'free'
    ...     def innerfunc():
    ...         var = 'inner'
    ...         print "Inside innerfunc, var: ", var
    ...         print "Inside innerfunc, locals(): ", locals()
    ...     innerfunc()
    ...     print "var: ", var
    ...     print "locals(): ", locals()
    ...
    >>> outerfunc()
    Inside innerfunc, var:  inner
    Inside innerfunc, locals():  {'var': 'inner'}
    var:  free
    locals():  {'var': 'free', 'innerfunc': <function innerfunc at 0x102cf0410>}
    >>>
    

    在innerfunc函数中我试图修改自由变量var的值,结果却发现修改只在innerfunc函数内有效,离开此函数后其值仍然是"free"。看看输出的outerfunc和innerfunc函数的本地变量列表就知道咋回事儿了。当你想在闭包函数中对自由变量重新赋值时,实际上你只是在这里定义了一个本地变量,这并不会改变自由变量的值。

    那怎样才能修改自由变量的值?Notes on Python variable scope 中找到了两种不错的方式:

    <a id="code-snippet-7">code snippet 7</a>

    class Namespace: pass
    def ex7():
        ns = Namespace()
        ns.var = 'foo'
        def inner():
            ns.var = 'bar'
            print 'inside inner, ns.var is ', ns.var
        inner()
        print 'inside outer function, ns.var is ', ns.var
    ex7()
    
    # console output
    inside inner, ns.var is  bar
    inside outer function, ns.var is  bar
    

    <a id="code-snippet-8">code snippet 8</a>

    def ex8():
        ex8.var = 'foo'
        def inner():
            ex8.var = 'bar'
            print 'inside inner, ex8.var is ', ex8.var
        inner()
        print 'inside outer function, ex8.var is ', ex8.var
    ex8()
    
    # console output
    inside inner, ex8.var is  bar
    inside outer function, ex8.var is  bar
    

    参考资源:

    1. python executin model
    2. Notes on Python variable scope
    3. Python Closures and Free Variables
    4. python模块的内置方法

    相关文章

      网友评论

          本文标题:python学习笔记 - local, global and f

          本文链接:https://www.haomeiwen.com/subject/poodxttx.html