理解 python 模块加载和路径查找

引用自理解 python 模块加载和路径查找

基础概念

  • module
    模块, 一个 py 文件或以其他文件形式存在的可被导入的就是一个模块
  • package
    包,包含有 init 文件的文件夹
  • relative path
    相对路径,相对于某个目录的路径
  • absolute path
    绝对路径,全路径
  • 路径查找
    python 解释器查找被引入的包或模块

python 执行时是如何查找包和模块的

执行一个 python 模块

python 执行一个文件,无论执行的方式是绝对路径还是相对路径,解释器都会把文件所在的目录加入到系统查找路径中,也就是sys.path 这个list中,而 sys.path 又是由系统的 python 环境变量决定的。

#test.py
import os
import sys
print sys.path[0]

执行这个文件:

python test.py
python /Users/x/workspace/blog-code/p2016_05_28_python_path_find
test.py

相对路径和绝对路径输出相同的结果。test.py 所在的文件夹都会被加入 sys.path 的首位,注意这里是首位,也就是索引为0的位置。

解释器执行时,首先搜索 built-in module ,也就是解释器查找模块最先查找的是built-in module ,其次才会搜索 sys.path所包含的路径。这样的查找顺序将会引起同名包或模块被遮蔽的问题。

├── os.py
├── test2.py
├── redis.py
#test2.py
import os
from redis import Redis

执行 test2.py 文件

Traceback (most recent call last):
    File "/Users/x/workspace/blog-code/p2016_05_28_python_path_find/test2.py", line 1, in <module>
        from redis import Redis
ImportError: cannot import name Redis

由于 os 是 built-in module ,即使在同目录下有同名模块,解释器依然可以找到正确的os模块,而 redis 属于第三方模块,默认安装位置是 python 环境变量中的 site-packages 下,解释器启动之后会将此目录加入 sys.path,按照上面所说的查找顺序,优先在执行文件所在的目录查找,由于其在 sys.path 的首位,因而本地的 redis 被导入。

交互式执行环境

进入交互式执行环境,解释器会自动把当前目录加入 sys.path, 这时当前目录是以相对路径的形式出现在 sys.path 中:

>>> import os.path
>>> import sys
>>> os.path.abspath(sys.path[0])
'/Users/x/workspace/blog-code'
>>>

除此之外,其他与执行一个文件是相同的。

理解 file 变量

python doc: file is the pathname of the file from which the module was loaded, if it was loaded from a file. 如果一个模块是从文件加载的,file 就是该模块的路径名。

顾名思义,当模块以文件的形式出现 file 指的是模块文件的路径名,以相对路径执行 file 是相对路径,以绝对路径执行 file 是绝对路径。

#test.py
print __file__

相对路径执行

python test3.py
test3.py

绝对路径执行

python /Users/x/workspace/blog-code/p2016_05_28_python_path_find/test3.py

为了保证file 每次都能准确得到模块的正确位置,最好再取一次绝对路径

os.path.abspath(__file__)

进入交互式 shell ,输入file 会报错误

>>> __file__
Traceback (most recent call last):
    File "<input>", line 1, in <module>
NameError: name '__file__' is not defined

这是因为当前交互式shell的执行并不是以文件的形式加载,所以不存在__file__ 这样的属性。

变量 sys.argv[0]

sys.argv[0]是它用来获取主入口执行文件的变量。

A sample

#test.py
import sys
print __file__
print sys.argv[0]

以上俩个print输出相同的结果,因为主执行文件和file所属的模块是同一个。当我们改变入口文件,区别就出现了。

A sample

#test.py
import sys
print __file__
print sys.argv[0]
#test2.py
import test

执行 test2.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 中供下次载入使用,这样可以加速模块的引入,起到缓存的作用。

>>> import sys
>>> sys.modules['tornado']
Traceback (most recent call last):
    File "<input>", line 1, in <module>
KeyError: 'tornado'
>>> import tornado
>>> sys.modules['tornado']
<module 'tornado' from '/Users/x/python_dev/lib/python2.7/site-packages/tornado/__init__.pyc'>

前面说过python 解释器启动之后,会把预先载入 built-in module,可以通过 sys.modules 验证。

>>> sys.modules['os']
<module 'os' from '/Users/x/python_dev/lib/python2.7/os.pyc'>
>>>

获取模块的路径

借助 sys.modules和file, 可以动态获取所有已加载模块目录和路径:

>>> import os
>>> os.path.realpath(sys.modules['os'].__file__)
'/Users/x/python_dev/lib/python2.7/os.pyc'
>>> import tornado
>>> os.path.realpath(sys.modules['tornado'].__file__)
'/Users/x/python_dev/lib/python2.7/site-packages/tornado/__init__.pyc'
def get_module_dir(name):
        path = getattr(sys.modules[name], '__file__', None)
        if not path
                raise AttributeError('module %s has not attribute __file__'%name)
        return os.path.dirname(os.path.abspath(path))

from 和 import 语句

知道了 python 是如何搜索模块和保存已导入模块,我们再说说 python 模块的导入

导入顺序

python 包的导入顺序是

系统包 --> 同目录 -- > sys.path

因此同名的包,系统包优先级最高 > 同目录 > sys.path,之所以有这样的差别是因为当前执行目录会优先加入sys.path 的首位。

导入时执行

当导入一个模块时,模块的顶层变量会被执行,包括全局变量,类以及函数的声明,因此在编写模块时一定要注意消除副作用(函数的调用)。

import 语句 和 from 语句

能被 import 的包括:package,package 中的模块,模块中的变量。影响 import 的属性是__all____all__ 是个list,能够影响被 package 中 以 from package import * 被导出的模块,即定义在__all__中的模块才会被 from package import *导出。

summary

python 的模块导入,加载和查找还有很多可以说说的地方,尤其是动态 import, 对应python中的关键字是 import,感兴趣的同学可以研究一下 tornado.util 模块下的 import_object

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容