转载自:http://www.lingcc.com/2011/12/15/11902/#sec-1python
平常使用python编程时,为了用某个代码模块,一般须要在代码中先import相应的module。
那么python的import是如何工做的呢?linux
对于大型的软件项目,模块化的管理很是有必要.
因而在现现在的面向对象语言中,都有相应的机制来应对这一问题.
如C++中的namespace, Java中的package,C#中的namespace和using.程序员
import就是Python中用于程序模块化管理的关键字.
经过import语句,将模块中声明或定义的变量或者函数等名字在当前程序运行的时刻可见.
这样咱们就能够直接经过名字的方式,如变量名或者函数名复用原有代码.编程
经过import语句,咱们就能将python代码模块化,方便管理和维护小程序
先看一组示例:dom
>>> path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'path' is not defined >>> sys.path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'sys' is not defined >>> import sys >>> path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'path' is not defined >>> sys.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> from sys import path >>> path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/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内建函数,用于查看指定做用域下可用的名字.
若没有传参数,则打印当前做用域下的可用名字.python2.7
>>> help(dir) Help on built-in function dir in module __builtin__: dir(...) dir([object]) -> list of strings If called without an argument, return the names in the current scope. Else, return an alphabetized list of names comprising (some of) the attributes of the given object, and of attributes reachable from it. If the object supplies a method named __dir__, it will be used; otherwise the default dir() logic is used and returns: 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 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了哪些名字.函数
再看一组示例:优化
>>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> import sys >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'sys'] >>> import sys as SYS >>> dir() ['SYS', '__builtins__', '__doc__', '__name__', '__package__', 'sys'] >>> SYS.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> sys.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> del(sys) >>> SYS.path ['', '/usr/lib/python2.7', '/usr/lib/python2.7/plat-linux2', '/usr/lib/python2.7/lib-tk', '/usr/lib/python2.7/lib-old', '/usr/lib/python2.7/lib-dynload', '/usr/local/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages', '/usr/lib/python2.7/dist-packages/PIL', '/usr/lib/python2.7/dist-packages/gst-0.10', '/usr/lib/python2.7/dist-packages/gtk-2.0', '/usr/lib/pymodules/python2.7'] >>> sys.path Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'sys' is not defined
上面的例子展现了两个功能:
有时咱们可能须要编写一个完整的模块库,好比python对XML的处理就须要一堆的函数.这时候可能划分红多个文件,更加方便管理.
逻辑上也更加清晰.
所以python引入了对多文件模块包的支持.说白了,就是import的不是一个文件的内容,而是一个文件夹的内容.
看下面的示例:
>>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] >>> import xml >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'xml'] >>> import xml.sax.xmlreader >>> dir() ['__builtins__', '__doc__', '__name__', '__package__', 'xml'] >>> dir(xml) ['_MINIMUM_XMLPLUS_VERSION', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'sax'] >>> dir(xml.sax) ['ContentHandler', 'ErrorHandler', 'InputSource', 'SAXException', 'SAXNotRecognizedException', 'SAXNotSupportedException', 'SAXParseException', 'SAXReaderNotAvailable', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', '_create_parser', '_exceptions', '_false', '_key', 'default_parser_list', 'handler', 'make_parser', 'parse', 'parseString', 'xmlreader'] >>> from xml import * >>> dir(xml) ['_MINIMUM_XMLPLUS_VERSION', '__all__', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '__path__', 'dom', 'etree', 'parsers', 'sax']
表面上看起来,和内容在单个文件内的import机制差很少. 咱们能够到xml对应的目录下看看:
erlv@erlv-debian:/usr/lib/python2.7/xml$ ls * __init__.py __init__.pyc __init__.pyo dom: domreg.py expatbuilder.py __init__.py minicompat.py minidom.py NodeFilter.py pulldom.py xmlbuilder.py etree: cElementTree.py ElementInclude.py ElementPath.py ElementTree.py __init__.py parsers: expat.py __init__.py sax: _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三个文件.
从上面的观察中能够看到,其实python的import机制完成的是名字做用域的相关操做.包括做用域的分层,提高和删除等等.
Python中的做用域是一个树状的结构,经过”.”操做,程序员能够进入做用域分支中找到想要的名字.
同时,能够经过from XXX import YYY机制实现将某个树枝上的名字提高到当前做用域中.
因此,python解释器在实现这种做用域机制的时候,须要引入做用域层级的概念.
另外,为了实现这套机制的动态支持,包括提高新名字,名字重命名和名字删除操做.
Python解释器采起了全局模块池的方式.全部的模块在加载后都添加到这个池中.
在经过链表的形式维护树状的逻辑结构.
python中灵活的做用域管理,一方面可让程序员更加方便的对代码进行模块化管理,另一方面也增长了灵活性,最大可能的减少当前做用域的名字污染问题.
参考2中的<python源码剖析>中,详细介绍了python解释器中如何支持import动做的.
这部分的实现主要在cpython解释器的import.c文件中.import动做的入口函数是bltinmodule.c的builtin__import__函数.