Python中__builtin__模块和__builtins__模块的区别和联系

来源:互联网 发布:jsp端口更改 编辑:程序博客网 时间:2024/04/30 08:11

摘要
    对于新手来说,经常混淆__builtin__模块和__builtins__模块,因此,本文简单介绍两个之间的区别和联系。

一、说明

   这里的说明主要是以Python2.7为例,因为在Python 3+中,__builtin__模块被命名为builtins,下面主要是探讨Python 2.x__builtin__模块和__builtins__模块的区别和联系。

二、__builtin__与__builtins__

1.名称空间(Namespace

   首先不得不说名称空间,因为名称空间是Python中非常重要的一个概念,所谓名称空间,其实指的是名称(标识符)到对象的映射。在一个正常的Python程序的执行过程中,至少存在两个名称空间:内建名称空间和全局名称空间如果定义了函数,则还会有局部名称空间全局名称空间一般由在程序的全局变量和它们对应的映射对象组成,而局部名称空间则在函数内部由函数局部变量和它们对应的映射对象组成。这里关键的是内建名称空间,它到底是怎么产生的?

2.内建函数

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

>>> abs(-1)

>>> max(1, 3)

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

>>> abs

<built-in function abs>

>>> 

>>> max

<built-in function max>

3.内建名称空间与__builtins__

    那么内建函数也是函数,虽然我们没有人为导入这些,但是正如前面所说,在启动Python解释器的时候,会自动帮我们导入,那么内建函数存在于哪里呢?其实准确地来说,是Python解释器在启动的时候会首先加载内建名称空间,内建名称空间有许多名字到对象之间映射,而这些名字其实就是内建函数的名称,对象就是这些内建函数本身(注意区分函数名称和函数对象的区别)。这些名称空间由__builtins__模块中的名字构成:

>>> dir()

['__builtins__', '__doc__', '__name__', '__package__']

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

>>> dir(__builtins__)

['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

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

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 "<stdin>", line 1, in <module>

ImportError: No module named __builtins__

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

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

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

5.__builtins____builtin__的深入区别

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

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__'

>>> __builtins__ == __builtin__

True

>>> __builtins__ is __builtin__

True

>>> id(__builtins__)

140295127423752

>>> id(__builtin__)

140295127423752

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

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

>>> def func():

...     print 'test'

... 

>>> func

<function func at 0x7f99012bdc08>

>>> funcs

<function func at 0x7f99012bdc08>

>>> func.__name__

'func'

>>> funcs.__name__

'func'

>>> funcs == func

True

>>> funcs is func

True

>>> id(funcs)

140295126375432

>>> id(func)

140295126375432

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

2)不是在主模块中

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

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

1

#!/usr/bin/env python

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__) 140592847690504

id(__builtins__) 140592847925608

==================================================

*==test __builtin__.__dict__ and __builtins__==*

__builtin__.__dict__ == __builtins__ True

__builtin__ is __builtins__ True

id(__builtin__) 140592847925608

id(__builtins__) 140592847925608

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

三、总结

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

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

如果不是在主模块中,那么__builtins__只是引用__builtin__.__dict__

1 0
原创粉丝点击