这里主要以Python 2.7为例来探讨Python 2.x中__builtin__模块和__builtins__模块的区别和联系!
PS:在Python 3+中,__builtin__模块被命名为builtins
名称空间(NameSpace)是Python中非常重要的一个概念,所谓命名空间其实就是从名称到对象的映射,大部分的命名空间都是通过Python 字典来实现的。
命名空间提供了在项目中避免名字冲突的一种方法,各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。
我们举一个计算机系统中的例子,一个文件夹(目录)中可以包含多个文件夹,每个文件夹中不能有相同的文件名,但不同文件夹中的文件可以重名:
一般有三种命名空间:
命名空间查找顺序:
假设我们要使用变量runoob,则Python 的查找顺序为:局部的命名空间去 -> 全局命名空间 -> 内置命名空间,如果找不到变量 runoob,它将放弃查找并引发一个 NameError 异常:
NameError: name 'runoob' is not defined
命名空间的生命周期:
命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束,因此,我们无法从外部命名空间访问内部命名空间的对象。
# var1 是全局名称
var1 = 5
def some_func():
# var2 是局部名称
var2 = 6
def some_inner_func():
# var3 是内嵌的局部名称
var3 = 7
如下图所示,相同的对象名称可以存在于多个命名空间中:
在启动Python解释器之后,即使没有创建任何的变量或者函数,还是会有许多函数可以使用,我们把这些函数称为内建函数,是因为它们不需要我们作任何定义,在启动Python解释器的时候,就已经导入到内存当中供我们使用:
内建函数也是函数,虽然我们没有人为导入这些,但是正如前面所说,在启动Python解释器的时候,会自动帮我们导入,那么内建函数存在于哪里呢?
其实准确地来说,是Python解释器在启动的时候会首先加载内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身(注意区分函数名称和函数对象的区别),这些名称空间由__builtins__模块中的名字构成。
下面我们在一个空文件里通过打印dir()来查看被自动导入的模块都有哪些:
可以看到有一个__builtins__的模块名称,这个模块本身定义了一个名称空间,即内建名称空间,我们不妨dir一下:
会看到我们熟悉的内建函数的名称,如list、dict等,当然还有一些异常和其它属性~
既然内建名称空间由__builtins__模块中的名称空间定义,那么是不是也意味着内建名称空间中所对应的这些函数也是在__builtins__模块中实现的呢?显然不是的,我们可以在解释器中直接输入__builtins__:
从结果中可以看到,__builtins__其实还是引用了__builtin__模块而已,这说明真正的模块是__builtin__,也就是说,前面提到的内建函数其实是在内建模块__builtin__中定义的,即__builtins__模块包含内建名称空间中内建名字的集合(因为它引用或者说指向了__builtin__模块),而真正的内建函数、异常和属性来自__builtin__模块,也就是说,在Python中,其实真正是只有__builtin__这个模块,并不存在__builtins__这个模块:
可以看到,导入__builtin__模块并没有问题,但导入__builtins__模块时就会提示不存在,这充分说明了前面的结论。
上面只是大概说了下__builtins__与__builtin__两个模块的简单区分而已,其实深究下去,要分成下面所提及的两种情况。
在主模块__main__中
其实我们在使用Python交互器的时候就是在主模块中进行操作,可以做如下验证:
在这种情况,__builtins__与__builtin__是完全一样的,它们指向的都是__builtin__这个内建模块:
从上面的结果可以看到,这时候__builtins__和__builtin__是完全一样的,它们都指向了同一个模块对象,这也就是Python中引用传递的概念,其实这种情况跟我们创建一个变量并对它做一次引用传递时的情况是一样的。
不在主模块__main__中
如果不是在主模块中使用__builtins__,这时候__builtins__只是对__builtin__.__dict__的一个简单引用而已,可以通过下面的测试来验证说明:
先创建一个test.py模块,代码如下所示:
import __builtin__
print 'Module name:', __name__
print '*==test __builtin__ and __builtins__==*'
print '__builtin__ == __builtins__', __builtin__ == __builtins__
print '__builtin__ is __builtins__', __builtin__ is __builtins__
print 'id(__builtin__)', id(__builtin__)
print 'id(__builtins__)', id(__builtins__)
print '=' * 50
print '*==test __builtin__.__dict__ and __builtins__==*'
print '__builtin__.__dict__ == __builtins__', __builtin__.__dict__ == __builtins__
print '__builtin__.__dict__ is __builtins__', __builtin__.__dict__ is __builtins__
print 'id(__builtin__)', id(__builtin__.__dict__)
print 'id(__builtins__)', id(__builtins__)
之后我们在Python交互器中导入它,那么这时候对于test模块来说,它就不是主模块了:
可以看到输出的结果跟我们想的是完全一样的,这时候__builtins__其实是对__builtin__.__dict__模块的引用
在启动Python解释器或运行一个Python程序时,内建名称空间都是从__builtins__模块中加载的,只是__builtins__本身是对Python内建模块__builtin__的引用,而这种引用又分下面两种情况:
参考链接:
https://docs.python.org/3/library/functions.html#abs
https://www.runoob.com/python3/python3-namespace-scope.html
https://blog.csdn.net/pzqingchong/article/details/77366084