您的位置:首页 > 编程语言 > Python开发

Python 变量,以及global和nonlocal关键字

2020-02-01 04:40 1066 查看

目录

  1. 变量作用域
  2. global
  3. 闭包与nonlocal

1. 变量作用域

作为一门动态语言,Python变量在使用的时候,不用申明变量类型,而是直接使用的。

>>> a = 123
>>> print(a)
123
>>> a = 'hello, Python'
>>> print(a)
hello, Python
>>>

当我们在命令行敲入python3的时候,当期模块默认为

__main__

>>> print(__name__)
__main__
>>>

接下来,定义一个函数,对于函数而言,前面的变量a是外层变量,可以理解为默认模块

__main__
的全局变量。
在函数foo中,直接访问变量a,最终在外层找到了a。如果在函数中给变量a赋值,会覆盖全局变量。
注意,这里相当于定义了一个局部变量a,而不是改写全局变量a。

>>> def foo():
...     print(a)
...
>>> foo()
hello, Python
>>>
>>> def foo():
...     a = 'in foo'
...     print(a)
...
>>> foo()
in foo
>>>
>>> print(a)
hello, Python
>>>

但是下面的方式会报错,因为Python在编译函数bar的时候,会扫描整个函数体,生成字节码,然后执行。在生成字节码的时候,发现

a=
这样的赋值语句,会认为定义了一个变量a,然后执行
print(a)
的时候,这时候
a = 'in bar‘
没有执行,故变量a没有任何绑定值,从而报错。

>>> def bar():
...     print(a)
...     a = 'in bar'
...
>>> bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in bar
UnboundLocalError: local variable 'a' referenced before assignment
>>>

2. global

上面函数bar中在执行第一条语句的时候,为什么不去访问全局变量a呢?
这是Python语法规定的,当在函数体中有赋值语句时,编译的时候就认为定义了局部变量,从而保证函数封装性。
如果非得要限定为全局变量,可以使用global关键字。但这种代码要小心,因为很容易就改变了全局变量。
如下代码,最后全局变量a变成了'in bar'。

>>> def bar():
...     global a
...     print(a)
...     a = 'in bar'
...     print(a)
...
>>> bar()
hello, Python
in bar
>>> print(a)
in bar
>>>

3. 闭包与nonlocal

在这里,不得不提下闭包这个概念。在看过很多概念性定义之后,最后在《你不知道的JavaScript》这本书中,终于有了实质性的收获:

当函数可以记住并访问所在的词法作用域时, 就产生了闭包, 即使函数是在当前词法作用域之外执行。

你没有看错,我引用了一本JavaScript书中的定义。抽象性的东西,很多都能通用。
如下代码,在内层函数add中,访问了外层函数中变量count。
每次调用fn都返回一个函数对象,该对象可以在当前全局模块环境中执行,也就是在fn函数词法作用域之外执行。

>>> def fn():
...     count = 0
...     def add(dt = 0):
...         r = count + dt
...         print(r)
...     return add
...
>>> fn()
<function fn.<locals>.add at 0x7efcd433e048>
>>> add = fn()
>>> add()
0
>>> add(1)
1
>>> add(123)
123
>>>
>>> some = fn()
>>> some()
0
>>> some(123)
123
>>>

但是,当我们想要在内层函数直接赋值给外层函数中的变量时,问题就来了。下面的代码,遇到了上面一样的错误。语句

count += dt
给count赋值了,Python认为定义了一个局部变量count,+=运算要先取到count的值,但此时内层函数中count没有绑定值,于是报错了。

>>> def fn():
...     count = 0
...     def add(dt = 0):
...         count += dt
...         print(count)
...     return add
...
>>> some = fn()
>>> some()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in add
UnboundLocalError: local variable 'count' referenced before assignment
>>>

可以使用nonlocal来解决这个问题。
注意如下每次函数对象调用的结果,每次调用fn返回的函数对象,该对象持有一份count变量副本,每次调用都针对当前函数对象。

>>> def fn():
...     count = 0
...     def add(dt = 0):
...         nonlocal count
...         count += dt
...         print(count)
...     return add
...
>>> some = fn()
>>> some()
0
>>> some(1)
1
>>> some(123)
124
>>> some(1)
125
>>>
>>> any = fn()
>>> any()
0
>>> any(1)
1
>>> any(1)
2
>>>

转载于:https://my.oschina.net/athantang/blog/1795481

  • 点赞
  • 收藏
  • 分享
  • 文章举报
chipizan9676 发布了0 篇原创文章 · 获赞 0 · 访问量 21 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: