深入理解Python中的__builtin__和__builtins__

0.说明

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


1.名称空间(Namespace)

        首先不得不说名称空间,因为名称空间是Python中非常重要的一个概念,所谓名称空间,其实指的是名称(标识符)到对象的映射。

        在一个正常的Python程序的执行过程中,至少存在两个名称空间:

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

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


2.内建函数

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


1

2

3

4

>>> abs(-1)

1

>>> max(13)

3

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


1

2

3

4

5

>>> abs

<built-in function abs>

>>> 

>>> max

<built-in function max>


3.内建名称空间与__builtins__

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

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


1

2

>>> dir()

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

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


1

2

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

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


4.__builtins__与__builtin__的简单区别

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

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


1

2

>>> __builtins__

<module '__builtin__' (built-in)>

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


1

2

3

4

5

>>> import __builtin__

>>> import __builtins__

Traceback (most recent call last):

  File "<stdin>", line 1in <module>

ImportError: No module named __builtins__

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

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

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


5.__builtins__与__builtin__的深入区别

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

(1)在主模块__main__中

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


1

2

>>> print __name__

__main__

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


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

>>> 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中引用传递的概念。

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


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#!/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模块,如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

>>> 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__模块的引用。


6.总结

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

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

        如果需要转载本文,请注明来自香飘叶子的51cto博客

        在写本文的时候,参考了下面的文章,只是这些文章并没有给出像上面我这样的测试,链接如下:

https://docs.python.org/2/library/__builtin__.html?highlight=_builtin__#module-__builtin__

http://www.52ij.com/jishu/665.html

时间: 2024-09-22 14:54:47

深入理解Python中的__builtin__和__builtins__的相关文章

深入理解Python中的ThreadLocal变量(下)

在上篇我们看到了 ThreadLocal 变量的简单使用,中篇对python中 ThreadLocal 的实现进行了分析,但故事还没有结束.本篇我们一起来看下Werkzeug中ThreadLocal的设计. Werkzeug 作为一个 WSGI 工具库,由于一些方面的考虑,并没有直接使用python内置的ThreadLocal类,而是自己实现了一系列Local类.包括简单的Local,以及在此基础上实现的LocalStack,LocalManager 和 LocalProxy.接下来我们一起来看

深入理解Python中的ThreadLocal变量(中)

在 深入理解Python中的ThreadLocal变量(上)中我们看到 ThreadLocal 的引入,使得可以很方便地在多线程环境中使用局部变量.如此美妙的功能到底是怎样实现的?如果你对它的实现原理没有好奇心或一探究竟的冲动,那么接下来的内容估计会让你后悔自己的浅尝辄止了. 简单来说,Python 中 ThreadLocal 就是通过下图中的方法,将全局变量伪装成线程局部变量,相信读完本篇文章你会理解图中内容的.(对这张图不眼熟的话,可以回顾下上篇)). 在哪里找到源码? 好了,终于要来分析

理解Python中的装饰器

文章先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: @makebold @makeitalic def say(): return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出的答案是: def makebold(fn): def wrapped(): return "<b>" + fn() + "</b>&q

进一步理解Python中的函数编程_python

我们最好从最难的问题开始:"到底什么是函数编程 (FP)?"一个答案可能会说 FP 就是您在使用例如 Lisp.Scheme.Haskell.ML.OCAML.Clean.Mercury.Erlang(或其它一些)语言进行编程时所做的.这是一个稳妥的答案,但不能很确切地阐明问题.不幸的是,即使是函数程序员他们自己也很难对 FP 究竟是什么有个一致的认识."盲人摸象"的故事用来形容这一情况似乎很合适.还可以放心地将 FP 与"命令编程"(使用例如

深入理解Python中的元类(metaclass)_python

译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理解,以及在什么情况下需要进行元编程.于是e-satis同学给出了神一般的回复,该回复获得了985点的赞同点数,更有人评论说这段回复应该加入到Python的官方文档中去.而e-satis同学本人在Stack Overflow中的声望积分也高达6

深入Python解释器理解Python中的字节码_python

我最近在参与Python字节码相关的工作,想与大家分享一些这方面的经验.更准确的说,我正在参与2.6到2.7版本的CPython解释器字节码的工作. Python是一门动态语言,在命令行工具下运行时,本质上执行了下面的步骤:     当第一次执行到一段代码时,这段代码会被编译(如,作为一个模块加载,或者直接执行).根据操作系统的不同,这一步生成后缀名是pyc或者pyo的二进制文件.     解释器读取二进制文件,并依次执行指令(opcodes). Python解释器是基于栈的.要理解数据流向,我

理解Python中的类与实例_python

面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的"对象",每个对象都拥有相同的方法,但各自的数据可能不同. 仍以Student类为例,在Python中,定义类是通过class关键字: class Student(object): pass class后面紧接着是类名,即Student,类名通常是大写开头的单词,紧接着是(object),表示该类是从哪个类继承下来的,继承的概念我们后面再

理解Python中函数的参数_python

 定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了.对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解. Python的函数定义非常简单,但灵活度却非常大.除了正常定义的必选参数外,还可以使用默认参数.可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码.默认参数 我们仍以具体的例子来说明如何定义函数的默认参数.先写一个计算x2的函数: def power(x

理解 Python 中s可变参数的 *args 和 **kwargs

使用默认参数的可变参数 Python是支持可变参数的,最简单的方法莫过于使用默认参数,例如: def test_defargs(one, two = 2): print 'Required argument: ', one print 'Optional argument: ', two test_defargs(1) # result: # Required argument: 1 # Optional argument: 2 test_defargs(1, 3) # result: # Re