转载自:http://www.lingcc.com/2011/12/15/11902/#sec-1

日常使用python编程时,为了用某个代码模块,通常需要在代码中先import相应的module。
那么python的import是如何工作的呢?

1 如何使用import

对于大型的软件项目,模块化的管理非常有必要.
于是在现如今的面向对象语言中,都有相应的机制来应对这一问题.
如C++中的namespace, Java中的package,C#中的namespace和using.

import就是Python中用于程序模块化管理的关键字.
通过import语句,将模块中声明或定义的变量或者函数等名字在当前程序运行的时刻可见.
这样我们就可以直接通过名字的方式,如变量名或者函数名复用原有代码.

通过import语句,我们就能将python代码模块化,方便管理和维护

2 import语句针对单个模块文件的工作方式

先看一组示例:

  1. >>> path
  2. Traceback (most recent call last):
  3. File "<stdin>", line 1, in <module>
  4.  
  5. NameError: name 'path' is not defined
  6. >>> sys.path
  7. Traceback (most recent call last):
  8. File "<stdin>", line 1, in <module>
  9.  
  10. NameError: name 'sys' is not defined
  11. >>> import sys
  12. >>> path
  13. Traceback (most recent call last):
  14. File "<stdin>", line 1, in <module>
  15.  
  16. NameError: name 'path' is not defined
  17. >>> sys.path
  18. ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk',
  19. '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload',
  20. '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages',
  21. '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10',
  22. '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']
  23.  
  24. >>> from sys import path
  25. >>> path
  26. ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk',
  27. '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload',
  28. '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages',
  29. '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10',
  30. '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']

这段代码中,我们尝试使用sys包中的path变量得到python默认查找模块的路径信息,只有在import sys之后,python解释器才能正确的找到该变量.
我们通过一个python内部函数dir()来看看python解释器如何找到名字的. dir()函数是python内建函数,用于查看指定作用域下可用的名字.
若没有传参数,则打印当前作用域下的可用名字.

  1. >>> help(dir)
  2. Help on built-in function dir in module __builtin__:
  3.  
  4. dir(...)
  5. dir([object]) -> list of strings
  6.  
  7. If called without an argument, return the names in the current scope.
  8. Else, return an alphabetized list of names comprising (some of) the attributes
  9. of the given object, and of attributes reachable from it.
  10. If the object supplies a method named __dir__, it will be used; otherwise
  11. the default dir() logic is used and returns:
  12. for a module object: the module's attributes. for a class object: its attributes, and recursively the attributes of its bases. for any other object: its attributes, its class's attributes, and
  13.  
  14. recursively the attributes of its class's base classes. >>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> import sys >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'sys'] >>> dir(sys) [ ..., 'modules', 'path', ... , 'version', 'version_info', 'warnoptions'] >>> from sys import path >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'path', 'sys']

执行import语句后,python解释器会将sys模块的名字添加到当前作用域中,这样就能直接通过sys.path得到python的搜索路径了.

注意到,我们还用了from sys import path语句.通过这条语句path就被直接提升到当前作用域中,这样path这个名字就能被直接使用了.
之所以有from,是为了更加精确的让某个模块中的某个名字在当前作用域可见.通过这种机制,程序员可以精确控制当前作用域的名字,防止作用域被不必要的名字污染.
另外,这种机制也避免了使用”.”来进行子成员的引用,减小程序员的输入.
这里需要提一句,虽然python提供了from XXX import *支持,能将XXX模块中的所有名字都提升到当前作用域中,但是要小心使用,因为程序员不能精确的知道到底import了哪些名字.

再看一组示例:

  1. >>> dir()
  2. ['__builtins__', '__doc__', '__name__', '__package__']
  3.  
  4. >>> import sys
  5. >>> dir()
  6. ['__builtins__', '__doc__', '__name__', '__package__', 'sys']
  7.  
  8. >>> import sys as SYS
  9. >>> dir()
  10. ['SYS', '__builtins__', '__doc__', '__name__', '__package__', 'sys']
  11.  
  12. >>> SYS.path
  13. ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk',
  14. '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload',
  15. '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages',
  16.  
  17. '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10',
  18. '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']
  19. >>> sys.path
  20. ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk',
  21. '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload',
  22. '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages',
  23. '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10',
  24. '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']
  25.  
  26. >>> del(sys)
  27. >>> SYS.path
  28. ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk',
  29. '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload',
  30. '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages',
  31. '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10',
  32. '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7']
  33.  
  34. >>> sys.path
  35. Traceback (most recent call last):
  36. File "<stdin>", line 1, in <module>
  37. NameError: name 'sys' is not defined

上面的例子展示了两个功能:

  • import XXX as YYY: 这个可以对模块实施重命名操作.
  • del(): 用于删除当前空间中不再使用的名字.当空间中出现很多不再需要的名字时,可以利用该函数进行清理.

3 import语句针对模块包的工作方式

有时我们可能需要编写一个完整的模块库,比如python对XML的处理就需要一堆的函数.这时候可能划分成多个文件,更加方便管理.
逻辑上也更加清晰.
因此python引入了对多文件模块包的支持.说白了,就是import的不是一个文件的内容,而是一个文件夹的内容.

看下面的示例:

  1. >>> dir()
  2. ['__builtins__', '__doc__', '__name__', '__package__']
  3.  
  4. >>> import xml
  5. >>> dir()
  6. ['__builtins__', '__doc__', '__name__', '__package__', 'xml']
  7.  
  8. >>> import xml.sax.xmlreader
  9. >>> dir()
  10. ['__builtins__', '__doc__', '__name__', '__package__', 'xml']
  11.  
  12. >>> dir(xml)
  13. ['_MINIMUM_XMLPLUS_VERSION', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'sax']
  14.  
  15. >>> dir(xml.sax)
  16. ['ContentHandler', 'ErrorHandler', 'InputSource', 'SAXException', 'SAXNotRecognizedException', 'SAXNotSupportedException',
  17. 'SAXParseException', 'SAXReaderNotAvailable', '__builtins__', '__doc__', '__file__', '__name__',
  18.  
  19. '__package__', '__path__', '_create_parser', '_exceptions', '_false', '_key', 'default_parser_list', 'handler',
  20. 'make_parser', 'parse', 'parseString', 'xmlreader']
  21.  
  22. >>> from xml import *
  23. >>> dir(xml)
  24. ['_MINIMUM_XMLPLUS_VERSION', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'dom', 'etree', 'parsers', 'sax']

表面上看起来,和内容在单个文件内的import机制差不多. 我们可以到xml对应的目录下看看:

  1. erlv@erlv-debian:/usr/lib/python2.7/xml$ ls *
  2. __init__.py __init__.pyc __init__.pyo
  3.  
  4. dom:
  5. domreg.py expatbuilder.py __init__.py minicompat.py minidom.py NodeFilter.py pulldom.py xmlbuilder.py
  6.  
  7. etree:
  8. cElementTree.py ElementInclude.py ElementPath.py ElementTree.py __init__.py
  9.  
  10. parsers:
  11. expat.py __init__.py
  12.  
  13. sax:
  14. _exceptions.py expatreader.py handler.py __init__.py saxutils.py xmlreader.py

我们import的xmlreader,它的路径是xml/sax/xmlreader.py,和import xml.sax.xmlreader相同.
这实际上也正是python解释器实际的动作.

注意到,每个文件夹下都有一个_init__.py文件.这个是模块包中的必须文件,它帮助python解释器将该目录识别成包.
没有此文件的文件夹,python解释器不会把它当模块包文件夹的.
_init__.py中一般会指定包中所有的模块,以及import此包时,需要预先import哪些包等初始化信息.当然,你可以往里面添加其他代码.
该脚本会在import 包时执行. 默认可以为空.

另外,还注意到有.py,.pyc和.pyo三个文件.

  • .py文件:Python源程序文件,文本文件
  • .pyc文件:编译成字节码的python文件,可以使用python解释器,或者调用pycompile模块生成该文件.
  • .pyo文件:进行一定编译优化的后的字节码文件.
  • 另外,还可以控制python解释器,去掉”docstrings”,即代码中的无关文档字符串.

4 总结及深入阅读

从上面的观察中可以看到,其实python的import机制完成的是名字作用域的相关操作.包括作用域的分层,提升和删除等等.
Python中的作用域是一个树状的结构,通过”.”操作,程序员可以进入作用域分支中找到想要的名字.
同时,可以通过from XXX import YYY机制实现将某个树枝上的名字提升到当前作用域中.
所以,python解释器在实现这种作用域机制的时候,需要引入作用域层级的概念.

另外,为了实现这套机制的动态支持,包括提升新名字,名字重命名和名字删除操作.
Python解释器采取了全局模块池的方式.所有的模块在加载后都添加到这个池中.
在通过链表的形式维护树状的逻辑结构.
python中灵活的作用域管理,一方面可以让程序员更加方便的对代码进行模块化管理,另外一方面也增加了灵活性,最大可能的减小当前作用域的名字污染问题.

参考2中的<python源码剖析>中,详细介绍了python解释器中如何支持import动作的.
这部分的实现主要在cpython解释器的import.c文件中.import动作的入口函数是bltinmodule.c的builtin__import__函数.

5 参考

[转] Python的import初探的更多相关文章

  1. Python 装饰器初探

    Python 装饰器初探 在谈及Python的时候,装饰器一直就是道绕不过去的坎.面试的时候,也经常会被问及装饰器的相关知识.总感觉自己的理解很浅显,不够深刻.是时候做出改变,对Python的装饰器做 ...

  2. python中import和from...import区别

    在python用import或者from...import来导入相应的模块.模块其实就是一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的模块导入到我们的程序中 ...

  3. Python中import的使用

    python中的import语句是用来导入模块的,在python模块库中有着大量的模块可供使用,要想使用这些文件需要用import语句把指定模块导入到当前程序中. import语句的作用 import ...

  4. python 的import机制2

    http://blog.csdn.net/sirodeng/article/details/17095591   python 的import机制,以备忘: python中,每个py文件被称之为模块, ...

  5. python之import机制

    1. 标准 import        Python 中所有加载到内存的模块都放在 sys.modules .当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将 ...

  6. python中import和from...import...的区别

    python中import和from...import...的区别: 只用import时,如import xx,引入的xx是模块名,而不是模块内具体的类.函数.变量等成员,使用该模块的成员时需写成xx ...

  7. python的import与from...import的不同之处

    在python用import或者from...import来导入相应的模块.模块其实就是一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的模块导入到我们的程序中 ...

  8. (原)python中import caffe提示no module named google.protobuf.internal

    转载请注明出处: http://www.cnblogs.com/darkknightzh/p/5993405.html 之前在一台台式机上在python中使用import caffe时,没有出错.但是 ...

  9. linux环境下 python环境import找不到自定义的模块

    linux环境下 python环境import找不到自定义的模块 问题现象: Linux环境中自定义的模块swport,import swport 出错.swport模块在/root/sw/目录下. ...

随机推荐

  1. 慕课网,vue高仿饿了吗ASP源码视频笔记

    1.源码笔记 我的源码+笔记(很重要):http://pan.baidu.com/s/1geI4i2Z 感谢麦子学院项目相关视频 2.参考资料 Vue.js官网(https://vuejs.org.c ...

  2. POJ-1088 滑雪 (记忆化搜索,dp)

    滑雪 Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 86318 Accepted: 32289 Description Mich ...

  3. POJ-2081 Terrible Sets(暴力,单调栈)

    Terrible Sets Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 4113 Accepted: 2122 Descrip ...

  4. Python:zip()函数

    zip()函数的定义 从参数中的多个迭代器取元素组合成一个新的迭代器: 返回:返回一个zip对象,其内部元素为元组:可以转化为列表或元组: 传入参数:元组.列表.字典等迭代器. zip()函数的用法 ...

  5. RuntimeError: Object: Could not open SDE workspace

    客户环境,linux 终端里,使用arcpy.ListUsers() 报错:RuntimeError: Object: Could not open SDE workspace 经检查,问题在于 该机 ...

  6. PL/SQL常用表达式及举例(二)

    使用LOOP循环 declare v_i number:=1; begin loop dbms_output.put_line('v_i='||v_i); exit when v_i>=3; v ...

  7. Photoshop制作倒影的两种方法

    图片加了倒影,画面立刻变得生动起来.而用PS,制作倒影是如此的方便. 素材1 将素材1导入文档,ctrl+J复制图层,编辑-变换-垂直翻转将翻转的图层拖至下方 为翻转的图层添加图层蒙版,选中渐变工具, ...

  8. 1128 - Greatest Parent---LightOj(LCA+离线算法)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1128 给你一颗树,树的每个节点都有一个权值,树根是节点0,权值为1,树中每个节点的权值 ...

  9. python 基础 列表生成式 生成器

    列表生成式 列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式 举个例子,要生成list [1, 2, 3, 4, 5, 6, 7, ...

  10. mac版 android studio问题解决

    1.mac安装android studio 解决方案:如果你是安装新手,可以下载androud studio boundls 和 安装环境的jdk就可以了,不需要单独在配置环境了,如果你有经验,可以单 ...