Python 模块的加载顺序
基本概念
module
模块, 一个 py 文件或以其他文件形式存在的可被导入的就是一个模块
package
包,包含有 init 文件的文件夹
relative path
相对路径,相对于某个目录的路径
absolute path
绝对路径,全路径
Python 解释器是如何查找包和模块的
Python 执行一个 py 文件,无论执行的方式是用绝对路径还是相对路径,interpreter 都会把文件所在的 directory 加入 sys.path 这个 list 中,并且是索引为 0 的位置。Python 就是在 sys.path 中查找包和模块的。
# test.py
# coding:utf-8
import sys
print(sys.path)
print('Now in main.py')
def hello():
print('michael hello')
if __name__ == '__main__':
hello()
# 执行 python test.py
$ python test.py
['/tmp/module-package/app', '/usr/lib64/python27.zip', '/usr/lib64/python2.7', '/usr/lib64/python2.7/plat-linux2', '/usr/lib64/python2.7/lib-tk', '/usr/lib64/python2.7/lib-old', '/usr/lib64/python2.7/lib-dynload', '/usr/lib64/python2.7/site-packages', '/usr/lib/python2.7/site-packages']
Now in test.py
michael hello
Python 解释器查找包的顺序
解释器查找包:
- 解释器会默认加载一些 modules,除了
sys.builtin_module_names
列出的内置模块之外,还会加载其他一些标准库,都存放在sys.modules
字典中。 - 然后就是搜索
sys.path
路径下的模块了。
In [3]: import sys
In [4]: print(sys.builtin_module_names)
('_abc', '_ast', '_codecs', '_collections', '_functools', '_imp', '_io', '_locale', '_operator', '_signal', '_sre', '_stat', '_string', '_symtable', '_thread', '_tracemalloc', '_warnings', '_weakref', 'atexit', 'builtins', 'errno', 'faulthandler', 'gc', 'itertools', 'marshal', 'posix', 'pwd', 'sys', 'time', 'xxsubtype', 'zipimport')
这样的查找顺序将会导致同名包或模块被遮蔽。
示例2:
# tree
$ tree . -L 1
.
├── __init__.py
├── name
├── os.py
├── test2.py
├── test.py
└── test.pyc
# test2.py
import os
from redis import Redis
from test import hello
print('Now in test2.py')
print(os.getcwd())
# 执行 python test2.py
$ python test2.py
Traceback (most recent call last):
File "test2.py", line 2, in <module>
from redis import Redis
ImportError: No module named redis
这里的 os
模块并不是是 built-in module
,上面已经将 sys.builtin_module_names
内容打印出来了。只是 Python 解释器启动时就加载到了 sys.modules
中缓存起来了。所以,即使在同目录下有同名模块,解释器依然是可以找到正确的 os
模块的!如果你在import os
之前,先执行del sys.modules['os']
,那么,标准模块 os
就会被同目录下的 os.py
屏蔽了。
redis
属于第三方模块,默认安装位置是 Python 环境变量中的 site-packages
,解释器启动之后,会将此目录加到 sys.path
,由于当前目录会在 sys.path
的首位,当前目录的 redis 优先被找到了,site-packages
中的 redis
模块被屏蔽了。
综上所述,搜索的一个顺序是:sys.modules
缓存 -> sys.path[0]
即当前目录查找 -> sys.path[1:]
路径查找。
同时发现,模块被加载的时候,其中非函数或类的语句,例如 print('hello')
、name=michael
等,是会在 import
的时候,默认就执行了。
交互式执行环境的查找顺序
交互执行环境,解释器会自动把当前目录加入到sys.path
,这一点和直接执行文件是一样的,但是这种方式下,sys.path[0]
是存储的当前目录的相对路径,而不是绝对路径。
In [4]: import sys
In [5]: sys.path[0]
Out[5]: ''
模块中的 __file__
变量
文件中的 __file__
当模块以文件的形式出现 file 指的是模块文件的路径名,以相对路径执行 file 是相对路径,以绝对路径执行 file 是绝对路径:
# test3.py
print __file__
# 执行 python test.py
$ python test3.py
test3.py
$ python /tmp/module-package/app/test3.py
/tmp/module-package/app/test3.py
交互式 Shell 中的 __file__
前交互式 Shell 的执行并不是以文件的形式加载,所以不存在 __file__
这样的属性:
In [8]: __file__
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-8-358d5687b810> in <module>()
----> 1 __file__
NameError: name '__file__' is not defined
sys.argv[0]
变量
sys.argv[0]
是获得入口执行文件路径,__file__
是真实被执行模块的文件路径。比如下面例子中,test2.py
就是入口执行文件,而 test.py
就是在 import
时真实被执行的模块
# test.py
print(__file__)
print(sys.argv[0])
# test2.py
import test
# 执行 python test2.py
/tmp/module-package/app/test.py # __file__
test2.py # sys.argv[0]
sys.modules
的作用
载入的模块存放在何处? 答案是 sys.modules
。 模块一经载入, Python 会把这个模块加入 sys.modules
中供下次载入使用,这样可以加速模块引入,起到缓存作用。sys.modules
是一个 dict
类型的值。
In [14]: sys.modules['requests']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-14-8aefaef0aed5> in <module>()
----> 1 sys.modules['requests']
KeyError: 'requests'
In [15]: import requests
In [16]: sys.modules['requests']
Out[16]: <module 'requests' from '/usr/lib/python2.7/site-packages/requests/__init__.pyc'>
# 没有预先引入 math,但是 sys.modules 中已经有这个键
In [18]: sys.modules['math']
Out[18]: <module 'math' from '/usr/lib64/python2.7/lib-dynload/math.so'>
需要注意的是, sys.modules['math']
尽管可以看到 math
键,但是,要使用它,还是需要显示 import math
之后才能使用的,因为那只是 Python 解释器后台缓存的,你不显示引入,本地空间还是不会去发现它。
总结
Python 通过查找 sys.path
来决定包的导入,Python解释器启动时加载的模块缓存 > 同级目录 > sys.path[1:]
。Python 中的特有属性 __file__
以及 sys.argv[0]
、sys.argv[0]
、sys.modules
可以帮助分析包的查找和导入过程。
解决这个问题,请教了大牛同事,果然一下子让我明白了。于是,自问自答了在 SegmentFault 上提的问题:
参考
- 三月沙-如何理解 Python 的模块查找原理与方式 本文内容主要参考,但是该文章中提到的
os
属于built-in moulde
的理解是有误的,本文中修正了理解。 - 构建一个模块的层级包
- The Python Standard Library
- Medium-Python 的 Import 陷阱
- CSDN-Python 模块搜索路径 提交了
PYTHONPATH
这个环境变量的作用 - 旷世的忧伤-Python 如何处理模块和包有相同名字的情况 来源:How python deals with module and package having the same name?
- librarybook-The sys module
- 官宣-System-specific parameters and functions
- 浅谈 Python 的模块导入
Python 模块的加载顺序的更多相关文章
- day16模块,导入模板完成的三件事,起别名,模块的分类,模块的加载顺序,环境变量,from...import语法导入,from...import *,链式导入,循环导入
复习 ''' 1.生成器中的send方法 -- 给当前停止的yield发生信息 -- 内部调用__next__()取到下一个yield的返回值 2.递归:函数的(直接,间接)自调用 -- 回溯 与 递 ...
- pytho模块的加载顺序
当前目录如果有同名的系统模块,那么当前目录的模块会被import,系统模块会被忽略,如: 1 ghostwu@ghostwu:~/python/module$ ls 2 import_test.py ...
- nodejs 模块加载顺序
nodejs 模块加载顺序 一.当引入模块的形式是 require('lt') 时(1).先找当前文件夹下的node_modules文件夹下的lt文件夹下的package.json 文件指定的main ...
- 跟着太白老师学python day10 名称空间,作用域和取值顺序,变量的加载顺序
名称空间分为3种: 1. 全局名称空间 2. 内置名称空间 3. 局部名称空间(临时) 作用域 全局作用域 1全局名称空间 2 内置名称空间 局部作用域 ...
- DOM加载顺序
最近一直在困扰dom的加载顺序问题,经常会遇到以为绑定好的事件不响应等情况,一头雾水,直到请教了周围的同事,才发现了解dom的加载顺序是多么的重要. 关于这个问题,其实网上已经有一些介绍,但是我觉得并 ...
- javascript不依赖JS加载顺序事件对象实现
背景: 在现在WEB开发中,稍复杂一点的页面,都会涉及到多个模块,尤其是类似seajs.LABjs.requireJS等模块工具出来后,前端开发者分模块开发已经慢慢变成一种习惯了,但是多个模块间的常常 ...
- Java中静态变量、静态代码块、非静态代码块以及静态方法的加载顺序
在研究单例设计模式的时候,用到了静态变量和静态方法的内容,出于兴趣,这里简单了解一下这四个模块在类初始化的时候的加载顺序. 经过研究发现,它们的加载顺序为: 1.非静态代码块 2.静态变量或者静态代码 ...
- SpringBoot系列教程之Bean加载顺序之错误使用姿势辟谣
在网上查询 Bean 的加载顺序时,看到了大量的文章中使用@Order注解的方式来控制 bean 的加载顺序,不知道写这些的博文的同学自己有没有实际的验证过,本文希望通过指出这些错误的使用姿势,让观文 ...
- ES6 模块的加载实现 import和export
ES6的Class只是面向对象编程的语法糖,升级了ES5的构造函数的原型链继承的写法,并没有解决模块化问题.Module功能就是为了解决这个问题而提出的. 历史上,JavaScript一直没有模块(m ...
随机推荐
- python 将文件大小转换为human readable 的大小表示
定义了一个函数, def HRS(size): units=('B','KB','MB','GB','TB','PB') for i in range(len(units)-1,-1,-1 ...
- 04-树6 Complete Binary Search Tree(30 分)
title: 04-树6 Complete Binary Search Tree(30 分) date: 2017-11-12 14:20:46 tags: - 完全二叉树 - 二叉搜索树 categ ...
- C++中overload(重载),override(覆盖),overwrite(重写/覆写)的区别
#include <cstdio> #include <cstdlib> class Base { public: #pragma region MyRegion1 //函数重 ...
- workerman 7272端口被占用
1/问题:workerman 7272端口被占用 2/策略: 1.查找被占用的端口 netstat -tln netstat -tln | grep 8083 netstat -tln 查看端口使用情 ...
- RobotFrameWork(十三)RobotFramework与loadrunner性能测试结合(基于Remote库)
一般我们进行完功能测试,都需要进行下性能测试,那么这章我来介绍下,RobotFramework与loadrunner性能测试的融合,即运行完自动化功能测试,借助RobotFramework的Remot ...
- Block 循环引用(上)
iOS的内存管理机制 Objective-C在iOS中不支持GC(垃圾回收)机制,而是采用的引用计数的方式管理内存. 引用计数:在引用计数中,每一个对象负责维护对象所有引用的计数值.当一个新的引用指向 ...
- Mysql Federated For Windows
[1]windows环境下打开federated (1)关闭.命令:mysql> net stop mysql (2)添加federated字段.在my.ini文件中添加一个字段,注意位于[my ...
- python 序列化,反序列化
附: pickle 有大量的配置选项和一些棘手的问题.对于最常见的使用场景,你不需要去担心这个,是如果你要在一个重要的程序中使用pickle 去做序列化的话,最好去查阅一下官方文档. https:// ...
- Log4J基础详解及示例大全(转)
log4j可以通过使用配置文件的方式进行配置. 配置步骤如下: 1.定义日志组件logger 每个logger都可以拥有一个或者多个appender,每个appender表示一个日志的输出目的地,比如 ...
- GLSL写vertex shader和fragment shader
0.一般来说vertex shader处理顶点坐标,然后向后传输,经过光栅化之后,传给fragment shader,其负责颜色.纹理.光照等等. 前者处理之后变成裁剪坐标系(三维),光栅化之后一般认 ...