深入理解 Python2 中的 __builtin__ 和 __builtins__

这里以 Python 2.7为例,探讨 __builtin__ 模块和 __builtins__ 模块的区别和联系。

在 Python3 中,__builtin__ 模块被命名为 builtins

1 名称空间(Namespace)

名称空间指的是名称(标识符)到对象的映射,在一个正常的Python程序的执行过程中,至少存在两个名称空间:

  • 内建名称空间
  • 全局名称空间

如果定义了函数,则还会有局部名称空间,全局名称空间一般由在程序的全局变量和它们对应的映射对象组成,而局部名称空间则在函数内部由函数局部变量和它们对应的映射对象组成,这里关键的是内建名称空间,它到底是怎么产生的?

2 内建函数

在启动 Python 解释器之后,即使没有创建任何的变量或者函数,还是会有许多函数可以使用,比如:

>>> abs(-1)
1
>>> max(1,2)
2
>>> abs
<built-in function abs>
>>> max
<built-in function max>

这些函数是内建函数,因为它们不需要我们程序员作任何定义,在启动 Python 解释器的时候,就已经导入到内存当中供我们使用

3 内建名称空间与 __builtins__

那么内建函数也是函数,虽然我们没有人为导入这些,但是正如前面所说,在启动 Python 解释器的时候,会自动帮我们导入,那么内建函数存在于哪里呢?

其实准确地来说,是 Python 解释器在启动的时候会首先加载内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身(注意区分函数名称和函数对象的区别)。这些名称空间由 __builtins__ 模块中的名字构成:

>>>  dir ()
[ '__builtins__' ,  '__doc__' ,  '__name__' ,  '__package__' ]

可以看到有一个 __builtins__ 的模块名称,这个模块本身定义了一个名称空间,即内建名称空间,我们不妨 dir 一下:

深入理解 Python2 中的 __builtin__ 和 __builtins___第1张图片

会看到我们熟悉的内建函数的名称,如 list、dict 等,当然还有一些异常和其它属性。

4 __builtins__ 与 __builtin__ 的简单区别

既然内建名称空间由 __builtins__ 模块中的名称空间定义,那么是不是也意味着内建名称空间中所对应的这些函数也是在 __builtins__ 模块中实现的呢?

显然不是的,我们可以在解释器中直接输入 __builtins__:

>>> __builtins__
<module '__builtin__' (built-in)>

结果中可以看到,__builtins__ 其实还是引用了 __builtin__ 模块而已,这说明真正的模块是 __builtin__,也就是说,前面提到的内建函数其实是在内建模块 __builtin__ 中定义的,即 __builtins__ 模块包含内建名称空间中内建名字的集合(因为它引用或者说指向了 __builtin__ 模块),而真正的内建函数、异常和属性来自 __builtin__ 模块。也就是说,在Python中,其实真正是只有 __builtin__ 这个模块,并不存在 __builtins__ 这个模块:

>>> import __builtin__
>>> import __builtins__
Traceback (most recent call last):
  File "", line 1, in <module>
ImportError: No module named __builtins__

可以看到,导入 __builtin__ 模块并没有问题,但导入 __builtins__ 模块时就会提示不存在,这充分说明了前面的结论,现在再次总结如下:

在 Python 中并没有 __builtins__这个模块,只有 __builtin__ 模块,__builtins__ 模块只是在启动 Python 解释器时,解释器为我们自动创建的一个到 __builtin__ 模块的引用

至于这种引用到底是怎么样,可以看下面的深入区别。

5 __builtins__ 与 __builtin__ 的深入区别

上面只是大概说了下 __builtins__ 与 __builtin__ 两个模块的简单区分而已,其实深究下去,要分成下面所提及的两种情况。

5.1 在主模块 __main__ 中

其实我们在使用 Python 交互器的时候就是在主模块中进行操作,可以做如下验证:

>>> print __name__
__main__

在这种情况,__builtins__ 与 __builtin__ 是完全一样的,它们指向的都是__builtin__ 这个内建模块

>>> import __builtin__
>>> __builtin__
<module '__builtin__' (built-in)>
>>> __builtins__
<module '__builtin__' (built-in)>
>>> __builtin__.__name__
'__builtin__'
>>> __builtins__.__name__
'__builtin__'
>>> __builtin__ == __builtins__
True
>>> __builtin__ is __builtins__
True
>>> id(__builtins__)
139655361473296
>>> id(__builtin__)
139655361473296

可以看到,这时候 __builtins__ 和 __builtin__ 是完全一样的,它们都指向了同一个模块对象,其实这也是 Python 中引用传递的概念。

其实这种情况跟我们创建一个变量并对它做一次引用传递时的情况是一样的,可以做如下测试:

>>> def func():
...     print "test"
... 
>>> funcs = func
>>> func
<function func at 0x7f040c21ccd0>
>>> funcs
<function func at 0x7f040c21ccd0>
>>> func.__name__
'func'
>>> funcs.__name__
'func'
>>> func == funcs
True
>>> func is funcs
True
>>> id(func)
139655360138448
>>> id(funcs)
139655360138448

显然,这完全验证了我们上面的结论。

5.2 不是在主模块中

如果不是在主模块中使用 __builtins__,这时候,__builtins__ 只是对__builtin__.__dict__ 的一个简单引用而已,可以通过下面的测试来验证说明。

先创建一个 test.py 模块,后面我们需要在 Python 交互器中导入它,那么这时候对于 test 模块来说,它就不是主模块了。如下:

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__ is __builtins__' , __builtin__.__dict__  is  __builtins__
print  'id(__builtin__)' ,  id (__builtin__.__dict__)
print  'id(__builtins__)' ,  id (__builtins__)

在 Python 交互器中导入上面这个 test 模块,如下:

>>> import test
Module name: test
*==test __builtin__ and __builtins__==*
__builtin__ == __builtins__ False
__builtin__ is __builtins__ False
id(__builtin__) 140032283536144
id(__builtins__) 140032283615600
==================================================
*==test __builtin__.__dict__ and __builtins__==*
__builtin__.__dict__ == __builtins__ True
__builtin__ is __builtins__ True
id(__builtin__) 140032283615600
id(__builtins__) 140032283615600

可以看到输出的结果跟我们想的是完全一样的,即这时候 __builtins__ 其实是对__builtin__.__dict__ 模块的引用

6 总结

不管怎么说,在启动 Python 解释器或运行一个 Python 程序时,内建名称空间都是从 __builtins__ 模块中加载的,只是 __builtins__ 本身是对 Python 内建模块 __builtin__ 的引用,而这种引用又分下面两种情况:

  • 如果是在主模块 __main__ 中,__builtins__ 直接引用 __builtin__ 模块,此时模块名 __builtins__ 与模块名 __builtin__ 指向的都是同一个模块,即内建模块(这里要注意变量名和对象本身的区别)
  • 如果不是在主模块中,那么 __builtins__ 只是引用了__builtin__.__dict__

你可能感兴趣的:(python)