设为首页 收藏本站
查看: 662|回复: 0

[经验分享] 深入理解Python中的__builtin__和__builtins__

[复制链接]

尚未签到

发表于 2018-8-4 06:12:04 | 显示全部楼层 |阅读模式
  0.说明
  这里的说明主要是以Python 2.7为例,因为在Python 3+中,__builtin__模块被命名为builtins,下面主要是探讨Python 2.x中__builtin__模块和__builtins__模块的区别和联系。
  1.名称空间(Namespace)
  首先不得不说名称空间,因为名称空间是Python中非常重要的一个概念,所谓名称空间,其实指的是名称(标识符)到对象的映射。
  在一个正常的Python程序的执行过程中,至少存在两个名称空间:

  •   内建名称空间
  •   全局名称空间
  如果定义了函数,则还会有局部名称空间,全局名称空间一般由在程序的全局变量和它们对应的映射对象组成,而局部名称空间则在函数内部由函数局部变量和它们对应的映射对象组成,这里关键的是内建名称空间,它到底是怎么产生的?
  2.内建函数
  在启动Python解释器之后,即使没有创建任何的变量或者函数,还是会有许多函数可以使用,比如:
>>> abs(-1)  
1
  
>>> max(1, 3)
  
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']
  会看到我们熟悉的内建函数的名称,如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 "<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模块来说,它就不是主模块了。如下:
#!/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__模块的引用。
  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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-546089-1-1.html 上篇帖子: 关于B+tree (附python 模拟代码) 下篇帖子: fedora下安装python
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表