python中模块查找的原理与方法详解
前言
本文主要给大家介绍了关于python模块查找的原理与方式,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍:
基础概念
module
模块,一个py文件或以其他文件形式存在的可被导入的就是一个模块
package
包,包含有__init__文件的文件夹
relativepath
相对路径,相对于某个目录的路径
absolutepath
绝对路径,全路径
路径查找
python解释器查找被引入的包或模块
Python解释器是如何查找包和模块的
Python执行一个py文件,无论执行的方式是用绝对路径还是相对路径,interpreter都会把文件所在的directory加入sys.path这个list中,Python就是在sys.path中查找包和模块的,sys.path中的内容本身又是又Python的环境变量决定。
code-1
#test.py importos importsys printsys.path[0] #execute pythontest.py python/Users/x/workspace/blog-code/p2016_05_28_python_path_find/test.py
执行表明相对路径和绝对路径都输出相同的结果,而且无论哪种执行方式,test.py所在的文件夹都会被加入sys.path的首位,也就是索引为0的位置。
Python解释器查找包的顺序是什么
解释器查找包,首先搜索built-inmodule,其次搜索sys.path,这样的查找顺序将会导致同名包或模块被遮蔽。
code-2
#ls ├──os.py ├──test2.py ├──redis.py #test2.py importos fromredisimportRedis #executetest2.py Traceback(mostrecentcalllast): File"/Users/x/workspace/blog-code/p2016_05_28_python_path_find/test2.py",line1,infromredisimportRedis ImportError:cannotimportnameRedis
由于os是built-inmodule,即使在同目录下有同名模块,解释器依然可以找到正确的os模块,可以证实built-inmodule不会被遮蔽,而redis属于第三方模块,默认安装位置是Python环境变量中的site-packages,解释器启动之后会将此目录中的内容加入sys.path,由于当前目录会在sys.path的首位,当前目录的redis优先被找到,site-packages中的redis模块被遮蔽了。
交互式执行环境的查找顺序
进入交互式执行环境,解释器会自动把当前目录加入sys.path,这时当前目录是以相对路径的形式出现在sys.path中:
>>>importos.path >>>importsys >>>os.path.abspath(sys.path[0]) '/Users/x/workspace/blog-code' >>>
除此之外,其他与执行一个文件是相同的。
模块中的__file__变量
__file__isthepathnameofthefilefromwhichthemodulewasloaded,ifitwasloadedfromafile.如果一个模块是从文件加载的,__file__就是该模块的路径名–PythonDoc:
顾名思义,当模块以文件的形式出现__file__指的是模块文件的路径名,以相对路径执行__file__是相对路径,以绝对路径执行__file__是绝对路径。
#test3.py print__file__ #相对路径执行 pythontest3.py test3.py #绝对路径执行 python/Users/x/workspace/blog-code/p2016_05_28_python_path_find/test3.py /Users/x/workspace/blog-code/p2016_05_28_python_path_find/test3.py
为了保证__file__每次都能准确得到模块的正确位置,最好对其再取一次绝对路径os.path.abspath(__file__)。
交互式shell中的__file__
>>>__file__ Traceback(mostrecentcalllast): File"",line1,inNameError:name'__file__'isnotdefined
这是因为当前交互式shell的执行并不是以文件的形式加载,所以不存在__file__这样的属性。
sys.argv[0]变量
sys.argv[0]是它用来获取主入口执行文件。
#test.py importsys print__file__ printsys.argv[0]
以上print输出相同的结果,因为主执行文件和__file__所属的模块是同一个,当我们改变入口文件,区别就出现了。
#test.py importsys print__file__ printsys.argv[0] #test2.py importtest #executetest2.py /Users/x/workspace/blog-code/p2016_05_28_python_path_find/child/test.py#__file__ test2.py#sys.argv[0]
总的来说,sys.argv[0]是获得入口执行文件路径,__file__是获得任意模块文件的路径。
sys.modules的作用
既然Python是在sys.path中搜索模块的,那载入的模块存放在何处?答案就是sys.modules。模块一经载入,Python会把这个模块加入sys.modules中供下次载入使用,这样可以加速模块的引入,起到缓存的作用。
>>>importsys >>>sys.modules['tornado'] Traceback(mostrecentcalllast): File"",line1,inKeyError:'tornado' >>>importtornado >>>sys.modules['tornado']
前面说过Python解释器启动之后,会把预先载入built-inmodule,可以通过sys.modules验证。
>>>sys.modules['os']>>>
借助sys.modules和__file__,可以动态获取所有已加载模块目录和路径。
>>>importos >>>os.path.realpath(sys.modules['os'].__file__) '/Users/x/python_dev/lib/python2.7/os.pyc' >>>importtornado >>>os.path.realpath(sys.modules['tornado'].__file__) '/Users/x/python_dev/lib/python2.7/site-packages/tornado/__init__.pyc'
defget_module_dir(name): path=getattr(sys.modules[name],'__file__',None) ifnotpath raiseAttributeError('module%shasnotattribute__file__'%name) returnos.path.dirname(os.path.abspath(path))
summary
总的来说,Python是通过查找sys.path来决定包的导入,并且系统包优先级>同目录>sys.path,Python中的特有属性__file__以及sys.argv[0],sys.modules都能帮助我们理解包的查找和导入概念,只要能正确理解sys.path的作用和行为,理解包的查找就不是难题了。
文中所有代码见:github
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。