首页 > 代码库 > 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