首页 > 代码库 > Python Namespace and Scope

Python Namespace and Scope

我感觉很多英文名词翻译过来可能会引起误解,因为大家的背景各异,看过的书也不一样,对名词的理解也有差异,为了表述不会引起歧义,对于一些名词,文中全都用英文。

name(也叫 identifier)只是 objects 的 Name。这个解释跟没有解释一样。但是在python中, 所有的东西都是一个 object。name 仅仅是对内存中 object 的引用。

比如,一个简单的赋值操作 a = 2,这里的 2 是保存在内存中的一个object,a 则是对 2 的引用。我们可以用 python内置的函数 id() 去得到内存中对象的地址: 比如下面的代码:

>>> a = 2
>>> id(a)
23159120
>>> id(2)
23159120
>>> 

从上面的代码我们可以看出,object 2 在内存中的地址是 23159120,a 指向的 object 内存地址也是 23159120, 它们代表的是同一个object。下面在看几个例子:

>>> a = 2
>>> id(a)
23159120
>>> a += 1
>>> id(a)
23159096
>>> id(3)
23159096
>>> id(2)
23159120
>>> b = 2
>>> id(b)
23159120

 

这里发生了什么?用一张图说话吧!

技术分享

1  a = 2, 在内存中创建一个 object,a 指向这个 object。当执行  a += 1,内存中产生一个新的 object 3,然后 a 指向 object 3 。所以 a 和 3 的内存地址是一样的。

2 b = 2,内存中已经存在 object 2了,直接将它的引用赋值给 b。

python 为什么这么做呢?效率!内存中已经存在2 这个object了,在创建一个是浪费内存,直接把2的引用给b就行了,没必要重新创建一个。因此python中的 name 可以引用任何类型的object。这个会给有过 C java scala 编程经验的人很大困惑。其实python中的name仅仅是对内存中object 引用,object 的类型是object,name 是不存在类型的。那么 name 指向的类型是如何确定的呢? python运行时确定,这个就是python的强大之处,体现了它动态的特性。

>>> a = 5
>>> a = ‘Hello World!‘
>>> a = [1,2,3]

上面的几个例子在c/c++肯定是错的,但是在python中这么写完全没问题。因为python中一切皆是object,因此function也是object,所以name也可以指向function。

>>> def printHell():
...     print ‘Hello‘
... 
>>> a = printHell()
Hello

 

那么什么是 namespace 呢?

已经讲过name是什么了,下面来谈谈 namespace。

简单的说, namespace 就是 name 集合。

在python中,你可以将 namespace 想象成  name 到 object 的映射。

不同的 namespace 可以同时存在,但是它们必须完全隔离开来,否则会造成冲突。

当我们启动python 解释器的时候会创建一个namespace,这个namespace含有python所有的内置 name,只要程序不退出,这个namespace就一直存在。因此我们在程序的任何地方都可以调用 id()和print()。每个module也创建它自己的global namespace。

这些不同的namespace是互相隔离的,因此,同样的 name 可以存在不同的module中而不会引起冲突。

module 中有各种各样 function 和 class。当调用一个 function 时,python会创建一个 local namespace, 这个function定义的所有 name 都在这个 local namespace中。同样的, class 和 function也是一样。

技术分享

 

python 变量 scope

程序中虽然存在各种各样的 namespace,但是我们的程序也许并不可能去访问每一个 namspace。这个就是 scope 的概念。

scope就是不需要任何前缀名就可以访问namespace中的name 的代码块。这句确实不好翻译,原文为:Scope is the portion of the program from where a namespace can be accessed directly without any prefix. 一共有三种scope:

1 当前function的scope,其中含有 local name

2 模块scope,有 global name

3 最外层的scope,有 内置的 name

 

当一个name出现在function中,python先在 local namespace中搜索它,然后是 global namespace,最后在 内置的 namespace,如果还找不到就报错。

 

def outer_function():
    b = 20
    def inner_func():
        c = 30

a = 10

上面的代码,a 在 global namespace 中,变量 b 在 function outer_function 的 local namespace 中, c 在 inner_function() 的local namespace 中。在 function  inner_function 中,c 是 local namespace,b 是  nonlocal , a 是 global,我们可以给c赋值,但是对于 b 和 c 则是只读。

在 inner_function 中如果我们试图给 b 赋予一个新的值,那么python会在 local namespace 中新建一个name  b,这个b和 out_function中的b是不同的。a 同理。请看下面这个例子:

>>> def out_function():
...     a = 20
...     def inner_function():
...             a = 30
...             print ‘a = %s‘ % a
...     inner_function()
...     print ‘a = %s‘ % a
... 
>>> out_function()
a = 30
a= 20
>>> a = 10
>>> out_function()
a = 30
a = 20
>>> print ‘a = %s‘ % a
a = 10

上面这个例子中的 a ,都是在各自不同 namespace 中。

 

如果我们将 a 声明为 global,则所有的赋值和引用操作都将指向 global a。

>>> def outer_function():
...     global a
...     a = 20
...     def inner_function():
...         global a
...         a = 30
...         print ‘a = %s‘ % a
...     inner_function()
...     print ‘a = %s‘ % a
... 
>>> a = 10
>>> outer_function()
a = 30
a = 30
>>> print(‘a =‘,a)
a = 30

因为在local namespace 中将a声明为 global,所以它会去global namespace中去寻找而不是自己重新创建,如果找不到就会报错。

 

 

本文翻译于:https://www.programiz.com/python-programming/namespace

 

Python Namespace and Scope