python 中使用 global 引发了莫名其妙的问题

举报
风吹稻花香 发表于 2021/06/05 00:41:34 2021/06/05
【摘要】 定义全局变量的方法: console=None def log_info(logstr): global console if console is None: console = logging.StreamHandler() # 定义console handler 原文:http://www.cnblogs....


定义全局变量的方法:
console=None
def log_info(logstr):

global console
if console is None:
console = logging.StreamHandler()  # 定义console handler

原文:http://www.cnblogs.com/backinfile/p/5952263.html

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
复制代码

  

  同时发现 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]: 

文章来源: blog.csdn.net,作者:网奇,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jacke121/article/details/77369183

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。