zoukankan      html  css  js  c++  java
  • python 中使用 global 引发了莫名其妙的问题

    哪里出问题了

      python 中,使用 global 会将全局变量设为本函数可用。同时,在函数内部访问变量会先本地再全局

      在嵌套函数中,使用 global 会产生不合常理的行为。

      上代码:

    In [96]: def x():
        b = 12
        def y():
            global a,b
            a = 1
            b = 2
        y()
        print "b =",b
       ....:     
    
    In [97]: a = 111
    
    In [98]: del b
    
    In [99]: x()
    b = 12
    
    In [100]: a
    Out[100]: 1
    
    In [101]: b
    Out[101]: 2

      而在函数 x() 中,没有用 global,此时的b使用本地。所以 print 会打印本地 b

      为什么会打印 12 ?还有 In[101]的 b 为 2 该怎么解释?

      

      y(),使用的 global 竟然没把 x() 的 b = 12 导进来。

      在函数 y() 中,语句  global a,b  使a,b扩展为全局,所以,在最高层,就算没有 b(In[98]),也会产生 b(In[101])。

      

      也就是说, global a,b ,会认为 a 和 b 是最外层的变量。

      再试一下:

       

    In [102]: def x():
        b = 12
        def y():
            global a,b
            a = 1
        y()      
        print "b =",b
       .....:     
    
    In [103]: a = 111
    
    In [104]: del b
    
    In [105]: x()
    b = 12
    
    In [106]: a
    Out[106]: 1
    
    In [107]: b
    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    <ipython-input-107-3b5d5c371295> in <module>()
    ----> 1 b
    
    NameError: name 'b' is not defined

      

      报错了! y() global b 后没赋值,顶层就没有 b。这说明,global 只是引入名称,并不做赋值等操作。

      global 不会管变量存不存在,只导入名称,对该名称的操作会反应到 ‘最高层名称空间‘。

      再来:

      

    In [109]: a = 111
    
    In [110]: del b
    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    <ipython-input-110-745f2abe7045> in <module>()
    ----> 1 del b
    
    NameError: name 'b' is not defined
    
    In [111]: def x():
        b = 12
        def y():
            global a,b
            a = 1
            print b
        y()
        print "b =",b
       .....:     
    
    In [112]: x()
    ---------------------------------------------------------------------------
    NameError                                 Traceback (most recent call last)
    <ipython-input-112-7354d77c61ac> in <module>()
    ----> 1 x()
    
    <ipython-input-111-c05fc67a1e82> in x()
          5         a = 1
          6         print b
    ----> 7     y()
          8     print "b =",b
          9 
    
    <ipython-input-111-c05fc67a1e82> in y()
          4         global a,b
          5         a = 1
    ----> 6         print b
          7     y()
          8     print "b =",b
    
    NameError: global name 'b' is not defined

        这就确定了 内层y() 的 global 不会 导入 x() 的东西。

       那么,内层函数怎么使用正确的外层函数的变量呢?

    解决内层函数参数传递问题

      

      一、

      首先,若只是取值,则不需要做任何处理。

    In [119]: def x():
       .....:     a = 12
       .....:     def y():
       .....:         print a
       .....:     y()
       .....:     
    
    In [120]: x()
    12
    
    In [121]: 

      在 y() 中,一旦为 a 赋值,a 立马变内部变量。

    In [121]: def x():
       .....:     a = 12
       .....:     def y():
       .....:         print "before a =",a
       .....:         a = 1
       .....:         print "then a =",a
       .....:     y()
       .....:     
    
    In [122]: x()
    before a =---------------------------------------------------------------------------
    UnboundLocalError                         Traceback (most recent call last)
    <ipython-input-122-7354d77c61ac> in <module>()
    ----> 1 x()
    
    <ipython-input-121-d8fbc0dba399> in x()
          5         a = 1
          6         print "then a =",a
    ----> 7     y()
          8 
    
    <ipython-input-121-d8fbc0dba399> in y()
          2     a = 12
          3     def y():
    ----> 4         print "before a =",a
          5         a = 1
          6         print "then a =",a
    
    UnboundLocalError: local variable 'a' referenced before assignment

      一旦在函数 y() 的某处给 a 赋值,则在赋值前,python 会认为 a 不存在。

      同时发现 python2 的 print 会一个一个的 输出。鉴于此,我又在 python3 中试了,发现他是 一起输出。但这不是本文重点,折叠之。

    In [7]: def x():
        a = 1
        def y():
            print("before a=",a)
            a = 10
            print("then a=",a)
        y()
       ...:     
    
    In [8]: x()
    ---------------------------------------------------------------------------
    UnboundLocalError                         Traceback (most recent call last)
    <ipython-input-8-7354d77c61ac> in <module>()
    ----> 1 x()
    
    <ipython-input-7-6e01e7317b24> in x()
          5         a = 10
          6         print("then a=",a)
    ----> 7     y()
          8 
    
    <ipython-input-7-6e01e7317b24> in y()
          2     a = 1
          3     def y():
    ----> 4         print("before a=",a)
          5         a = 10
          6         print("then a=",a)
    
    UnboundLocalError: local variable 'a' referenced before assignment
    View Code

      

      同时发现 python代码运行前 会先扫一遍代码的,而不是单纯的一行一行的执行。

      同时发现返回 UnboundLocalError,而不是 NameError。注意到 ’unbound‘,这是官方概念。用 ’unbound‘ 来描述就是:global 会将顶层变量名称 绑定 到本地变量名称,同时变化,是为 ’引用‘;python 检测到 a = 1时,意识到 a 是本地的,所以 在 a ’指向一个对象‘(因为python变量均为引用),之前 ,调用 a 是非法 行为,但这种行为区别于于 NameError,就定义为 unbound local。

      二、

      使用 可变变量,如 list,dict

      

    In [127]: def x():
       .....:     l = ["in msg"]
       .....:     def y():
       .....:         msg = l[0]
       .....:         print "msg =",msg
       .....:         l[:] = ["out msg"]
       .....:     y()
       .....:     print l[0]
       .....:     
    
    In [128]: x()
    msg = in msg
    out msg

      

      没有报错,完美!

      要注意 语句  l[:] = ["out msg"] ,使用切片赋值,否则,

    In [129]: def x():
        l = ["in msg"]
        def y():
            msg = l[0]
            print "msg =",msg
            l = ["out msg"]
        y()
        print l[0]
       .....:     
    
    In [130]: x()
    ---------------------------------------------------------------------------
    UnboundLocalError                         Traceback (most recent call last)
    <ipython-input-130-7354d77c61ac> in <module>()
    ----> 1 x()
    
    <ipython-input-129-d44e750e285f> in x()
          5         print "msg =",msg
          6         l = ["out msg"]
    ----> 7     y()
          8     print l[0]
          9 
    
    <ipython-input-129-d44e750e285f> in y()
          2     l = ["in msg"]
          3     def y():
    ----> 4         msg = l[0]
          5         print "msg =",msg
          6         l = ["out msg"]
    
    UnboundLocalError: local variable 'l' referenced before assignment

      又出 UnboundLocalError 了,因为 第六行代码 为 l 分配了 一个新的 list。

      

      三、

      利用参数传递。

      

    In [136]: def x():
       .....:     a, b = 1, 2
       .....:     def y(a = a, b = b):
       .....:         a, b = 3, 4
       .....:         return a, b
       .....:     a, b = y()
       .....:     print a, b
       .....:     
    
    In [137]: x()
    3 4

      注意,不要在默认参数上放 list等可变对象。

     四、

      python3 引入了 nonlocal 关键词 来调用上一层变量。>_<

      来测试一下吧:

      

    In [16]: def x():
       ....:     a = 1
       ....:     def y():
       ....:         def z():
       ....:             nonlocal a
       ....:             a = 2
       ....:         z()
       ....:     y()
       ....:     print(a)
       ....:     
    
    In [17]: x()
    2

      

      看来 nonlocal 是依次向上申请名称。

      

    In [25]: def x():
        a = 2
        def y():
            a = 3
            def z():
                nonlocal a
                print("in z a =",a)
                a = 4
            z()
            print("in y a =", a)
        y()
        print("in x a =", a)
       ....:     
    
    In [26]: x()
    in z a = 3
    in y a = 4
    in x a = 2
    
    In [27]: 

      

      至此,问题完美解决。

      原创,欢迎指正。

      本文完。

  • 相关阅读:
    关于树及其各种操作
    正交矩阵与齐次矩阵
    矩阵的行列式与矩阵的逆
    线性变换
    矩阵
    向量的运算
    求两直线的交点
    微任务防抖
    二项分布
    二项式定理
  • 原文地址:https://www.cnblogs.com/backinfile/p/5952263.html
Copyright © 2011-2022 走看看