深入探讨Python的import机制:实现远程导入模块 | CSDN博文精选
- Regular packages:是一个带有 __init__.py 文件的文件夹,此文件夹下可包含其他子包,或者模块
- Namespace packages
__path__
属性不使用普通的列表。而是使用定制的可迭代类型,如果其父包的路径 (或者最高层级包的 sys.path) 发生改变,这种对象会在该包内的下一次导入尝试时自动执行新的对包部分的搜索。
parent/__init__.py
文件。实际上,在导入搜索期间可能找到多个 parent 目录,每个都由不同的部分所提供。因此 parent/one 的物理位置不一定与 parent/two 相邻。在这种情况下,Python 将为顶级的 parent 包创建一个命名空间包,无论是它本身还是它的某个子包被导入。
1.2 相对/绝对对导入
- 相对导入(relative import ):import foo.bar 或者 form foo import bar
- 绝对导入(absolute import):from . import B 或 from ..A import B,其中.表示当前模块,..表示上层模块
- 当你在开发维护自己的项目时,应当使用相对路径导入,这样可以避免硬编码带来的麻烦。
- 而使用绝对路径,会让你模块导入结构更加清晰,而且也避免了重名的包冲突而导入错误。
1.3 导入的标准写法
- import 语句应当分行书写
import os,sys
# good
import os
import sys
- import语句应当使用absolute import
from ..bar import Bar
# good
from foo.bar import test
- import语句应当放在文件头部,置于模块说明及docstring之后,全局变量之前
- import语句应该按照顺序排列,每组之间用一个空格分隔,按照内置模块,第三方模块,自己所写的模块调用顺序,同时每组内部按照字母表顺序排列
import os
import sys
# 第三方模块
import flask
# 本地模块
from foo import bar
2. __import__ 的妙用
importlib.import_module()
和 __import__()
等。
__import__
,普通的开发者,可能就会比较陌生。
__import__
是一个函数,也正是因为这个原因,使得 __import__
的使用会更加灵活,常常用于框架中,对于插件的动态加载。
__import__
,请看如下两种导入方法,他们是等价的。
import os
# 使用 __import__
os = __import__('os')
import pandas as pd
# 使用 __import__
pd = __import__('pandas')
__import__
常常用于插件的动态,事实上也只有它能做到(相对于 import 来说)。
插件
通常会位于某一特定的文件夹下,在使用过程中,可能你并不会用到全部的插件,也可能你会新增插件。
plugin01
、plugin02
、plugin03
、plugin04
四个插件,这些插件下都会实现一个核心方法 run()
。但有时候我不想使用全部的插件,只想使用 plugin02
、plugin04
,那我就在配置文件中写我要使用的两个插件。
custom_plugins=['plugin02', 'plugin04']
for plugin in conf.custom_plugins:
__import__(plugin)
sys.modules[plugin].run()
3. 理解模块的缓存
import
导入模块时,它会先检索 sys.modules
里是否已经载入这个模块了,如果已经载入,则不会再次导入,如果不存在,才会去检索导入这个模块。
my_mod02
这个模块里,我 import 两次 my_mod01
这个模块,按逻辑每一次 import 会一次 my_mod01
里的代码(即打印 in mod01
),但是验证结果是,只打印了一次。
print('in mod01')
$ cat my_mod02.py
import my_mod01
import my_mod01
$ python my_mod02.py
in mod01
sys.modules
的存在。
sys.modules
是一个字典(key:模块名,value:模块对象),它存放着在当前 namespace 所有已经导入的模块对象。
import sys
print(sys.modules.get('json', 'NotFound'))
import json
print(sys.modules.get('json', 'NotFound'))
sys.modules
才有了 json 模块的对象。
NotFound
<module 'json' from 'C:\Python27\lib\json\__init__.pyc'>
my_mod02.py
改写成如下
import importlib
import my_mod01
importlib.reload(my_mod01)
my_mod01.py
in mod01
in mod01
4. 查找器与加载器
sys.modules
找不到,则将发起调用 Python 的导入协议以查找和加载该模块。
查找器
和 加载器
。
- 由查找器实现的模块查找
- 由加载器实现的模块加载
4.1 查找器是什么?
>>> import sys
>>> sys.meta_path
[]
>>>
>>> import sys
>>> sys.meta_path
[<class '_frozen_importlib.BuiltinImporter'>,
<class '_frozen_importlib.FrozenImporter'>,
<class '_frozen_importlib_external.PathFinder'>,
<class '_frozen_importlib_external.PathFinder'>]
>>>
- 一种知道如何导入内置模块
- 一种知道如何导入冻结模块
- 一种知道如何导入来自 import path 的模块 (即 path based finder)。
- 定义一个实现了 find_module 方法的类(py2和py3均可),或者实现 find_loader 类方法(仅 py3 有效),如果找到模块需要返回一个 loader 对象或者 ModuleSpec 对象(后面会讲),没找到需要返回 None
- 定义完后,要使用这个查找器,必须注册它,将其插入在 sys.meta_path 的首位,这样就能优先使用。
class MyFinder(object):
@classmethod
def find_module(cls, name, path, target=None):
print("Importing", name, path, target)
# 将在后面定义
return MyLoader()
# 由于 finder 是按顺序读取的,所以必须插入在首位
sys.meta_path.insert(0, MyFinder)
+-- Finder (deprecated)
+-- MetaPathFinder
+-- PathEntryFinder
4.2 加载器是什么?
load_module()
的方法。
+-- Finder (deprecated)
| +-- MetaPathFinder
| +-- PathEntryFinder
+-- Loader
+-- ResourceLoader --------+
+-- InspectLoader |
+-- ExecutionLoader --+
+-- FileLoader
+-- SourceLoader
- 定义一个实现了 load_module 方法的类
- 对与导入有关的属性(点击查看详情)进行校验
- 创建模块对象并绑定所有与导入相关的属性变量到该模块上
- 将此模块保存到 sys.modules 中(顺序很重要,避免递归导入)
- 然后加载模块(这是核心)
- 若加载出错,需要能够处理抛出异常( ImportError),若加载成功,则返回 module 对象
4.3 模块的规格说明
__spec__
属性对外公开。有关模块规格的详细内容请参阅 ModuleSpec
。
- 模块名
- 加载器
- 模块绝对路径
import my_mod01
print(my_mod01.__spec__)
$ python3 my_mod02.py
in mod01
ModuleSpec(name='my_mod01', loader=<_frozen_importlib_external.SourceFileLoader object at 0x000000000392DBE0>, origin='/home/MING/my_mod01.py')
name='python'
import my_info
print(my_info.name)
# 加一个断点
import pdb;pdb.set_trace()
# 再加载一次
my_info.__spec__.loader.load_module()
print(my_info.name)
main.py
处,我加了一个断点,目的是当运行到断点处时,我修改 my_info.py 里的 name 为 ming
,以便验证重载是否有效?
python
> /home/MING/main.py(9)<module>()
-> my_info.__spec__.loader.load_module()
(Pdb) c
ming
4.4 导入器是什么?
5. 远程导入模块
5.1 动手实现导入器
- 一种是实现自己的元路径导入器;
- 另一种是编写一个钩子,添加到sys.path_hooks里,识别特定的目录命名模式。
- MetaPathFinder
- PathEntryFinder
find_module()
方法,而 Python 3.4+ 版,则推荐使用 find_spec()
方法,但这并不意味着你不能使用 find_module()
,但是在没有 find_spec()
方法时,导入协议还是会尝试 find_module()
方法。
find_module()
该如何写。
class UrlMetaFinder(abc.MetaPathFinder):
def __init__(self, baseurl):
self._baseurl = baseurl
def find_module(self, fullname, path=None):
if path is None:
baseurl = self._baseurl
else:
# 不是原定义的url就直接返回不存在
if not path.startswith(self._baseurl):
return None
baseurl = path
try:
loader = UrlMetaLoader(baseurl)
# loader.load_module(fullname)
except Exception:
return None
find_spec()
,要注意此方法的调用需要带有两到三个参数。
foo.bar.baz
。第二个参数是供模块搜索使用的路径条目。对于最高层级模块,第二个参数为 None
,但对于子模块或子包,第二个参数为父包 __path__
属性的值。如果相应的 __path__
属性无法访问,将引发 ModuleNotFoundError
。第三个参数是一个将被作为稍后加载目标的现有模块对象。导入系统仅会在重加载期间传入一个目标模块。
from importlib.machinery import ModuleSpec
class UrlMetaFinder(abc.MetaPathFinder):
def __init__(self, baseurl):
self._baseurl = baseurl
def find_spec(self, fullname, path=None, target=None):
if path is None:
baseurl = self._baseurl
else:
# 不是原定义的url就直接返回不存在
if not path.startswith(self._baseurl):
return None
baseurl = path
try:
loader = UrlMetaLoader(baseurl)
return ModuleSpec(fullname, loader, is_package=loader.is_package(fullname))
except Exception:
return None
- FileLoader
- SourceLoader
- get_code:获取源代码,可以根据自己场景实现实现。
- exec_module:执行源代码,并将变量赋值给 module.dict
- get_data:抽象方法,必须实现,返回指定路径的字节码。
- get_filename:抽象方法,必须实现,返回文件名
load_module()
,而这个方法早已在 Python 3.4 的时候就被废弃了,当然为了兼容考虑,你若使用 load_module()
也是可以的。
class UrlMetaLoader(abc.SourceLoader):
def __init__(self, baseurl):
self.baseurl = baseurl
def get_code(self, fullname):
f = urllib2.urlopen(self.get_filename(fullname))
return f.read()
def load_module(self, fullname):
code = self.get_code(fullname)
mod = sys.modules.setdefault(fullname, imp.new_module(fullname))
mod.__file__ = self.get_filename(fullname)
mod.__loader__ = self
mod.__package__ = fullname
exec(code, mod.__dict__)
return None
def get_data(self):
pass
def execute_module(self, module):
pass
def get_filename(self, fullname):
return self.baseurl + fullname + '.py'
- execute_module 必须重载,而且不应该有任何逻辑,即使它并不是抽象方法。
- load_module,需要你在查找器里手动执行,才能实现模块的加载。。
execute_module()
和 create_module()
。由于基类里已经实现了 execute_module
和 create_module()
,并且满足我们的使用场景。我这边可以不用重复实现。和旧模式相比,这里也不需要在设查找器里手动执行 execute_module()
。
class UrlMetaLoader(importlib.abc.SourceLoader):
def __init__(self, baseurl):
self.baseurl = baseurl
def get_code(self, fullname):
f = urllib2.urlopen(self.get_filename(fullname))
return f.read()
def get_data(self):
pass
def get_filename(self, fullname):
return self.baseurl + fullname + '.py'
finder = UrlMetaFinder(address)
sys.meta_path.append(finder)
import sys
import importlib
import urllib.request as urllib2
class UrlMetaFinder(importlib.abc.MetaPathFinder):
def __init__(self, baseurl):
self._baseurl = baseurl
def find_module(self, fullname, path=None):
if path is None:
baseurl = self._baseurl
else:
# 不是原定义的url就直接返回不存在
if not path.startswith(self._baseurl):
return None
baseurl = path
try:
loader = UrlMetaLoader(baseurl)
except Exception:
return None
class UrlMetaLoader(importlib.abc.SourceLoader):
def __init__(self, baseurl):
self.baseurl = baseurl
def get_code(self, fullname):
f = urllib2.urlopen(self.get_filename(fullname))
return f.read()
def get_data(self):
pass
def get_filename(self, fullname):
return self.baseurl + fullname + '.py'
def install_meta(address):
finder = UrlMetaFinder(address)
sys.meta_path.append(finder)
5.2 搭建远程服务端
http.server
模块用一条命令即可实现。
$ cat>my_info.py<EOF
name='Python编程时光'
print('ok')
EOF
$ cat my_info.py
name='Python编程时光'
print('ok')
$
$ python3 -m http.server 12800
Serving HTTP on 0.0.0.0 port 12800 (http://0.0.0.0:12800/) ...
...
>>> install_meta('http://localhost:12800/') # 往 sys.meta_path 注册 finder
>>> import my_info # 打印ok,说明导入成功
ok
>>> my_info.name # 验证可以取得到变量
'Python编程时光'
6. 参考文档
◆
精彩推荐
◆
推荐阅读
深入探讨Python的import机制:实现远程导入模块 | CSDN博文精选相关推荐
- 朋友圈装死,微博蹦迪,Python教你如何掌握女神情绪变化 | CSDN博文精选
作者 | A字头 来源 | 数据札记倌 很多人都是在朋友圈装死,微博上蹦迪. 微信朋友圈已经不是一个可以随意发表心情的地方了,微博才是! 所以你不要傻傻盯着女神的朋友圈发呆啦! 本文教你如何用Pyth ...
- Python识别文字,实现看图说话 | CSDN博文精选
作者 | 张小腿 来源 | CSDN博客 现在写文件很多网站都不让复制了,所以每次都是截图然后发到QQ上然后用手机QQ的文字识别再发回电脑.感觉有点小麻烦了,所以想自己写一个小软件方便方便自己,就有了 ...
- python图片识别论文_Python识别文字,实现看图说话 | CSDN博文精选
原标题:Python识别文字,实现看图说话 | CSDN博文精选 作者 | 张小腿 来源 | CSDN博客 现在写文件很多网站都不让复制了,所以每次都是截图然后发到QQ上然后用手机QQ的文字识别再发回 ...
- 一文搞懂 Python 的 import 机制
一.前言 希望能够让读者一文搞懂 Python 的 import 机制 1.什么是 import 机制? 通常来讲,在一段 Python 代码中去执行引用另一个模块中的代码,就需要使用 Python ...
- 万字长文详解如何用Python玩转OpenGL | CSDN 博文精选
作者 | 天元浪子 来源 | CSDN博文精选 [编者按]OpenGL(开放式图形库),用于渲染 2D.3D 矢量图形的跨语言.跨平台的应用程序编程接口,C.C++.Python.Java等语言都能支 ...
- python多级目录import_深入理解Python中import机制
大型项目中为了维护方便,通常使用模块化开发,模块化的过程中,就会涉及到各种包或者模块的相互导入,即使是对于有多个项目的Python开发者来说, import 也会让人困惑!本文带你深入了解python ...
- import的用法python_Python导入模块,Python import用法(超级详细)
Python导入模块,Python import用法(超级详细) 使用 Python 进行编程时,有些功能没必须自己实现,可以借助 Python 现有的标准库或者其他人提供的第三方库.比如说,在前面章 ...
- 关于Python的import循环嵌套问题、模块的__name__属性的一些实验结果与心得
上代码 话不多说,直接上代码.有A.py文件和B.py文件,其中分别有类Aclass和类Bclass. 首先是A.py的代码,也是后面我要运行的程序入口. import sys print(" ...
- python 怎么import自己写的py模块
今天给大家说以下怎么import自己写的自定义py模块文件.我就以一个简单的例子讲一下. 我的自定义模块为放在桌面上的名叫udf.py的文件.代码很简单,如下就是一个加和的自定义函数: 那我要怎么样可 ...
最新文章
- 如何发表一篇好的文章
- JavaScript初学者编程题(9)
- NSThread的使用
- [ZJOI2010] 基站选址(线段树优化dp)
- 极光推送android点击跳转页面,app关闭时点击推送消息实现页面跳转
- struts2下的Action配置的各项默认值
- SharePoint 2010设计(Design)权限能操作的网站操作菜单项
- think-cli脚手架快速搭建单模块VS多模块项目示例
- python3种基本数字类型_Python3基本数据类型
- 女儿傻 女儿悲 2014-2-23
- QGIS 3. 使用qgis制作三维浮雕地图
- php 拉丁文转中文,拉丁文在线翻译_拉丁语在线翻译
- 高斯过程回归预测 C++代码实现
- 对比UltraCompare和Beyond Compare我这么选,你会怎么选?
- Python 文件的读写操作
- qq飞车手游微信24区服务器,QQ飞车手游手游开服表_QQ飞车手游手游开服时间表_新服新区预告_第一手游网...
- 医疗管理系统-检查组管理
- 天猫商城自动化python脚本(仅供初学者学习使用)
- Failed to process package ‘cartographer_ros‘ :
- 阳光/海浪/沙滩/美女/泳装——51CTO.com两周年出游
热门文章
- Mysql使用大全 从基础到存储过程
- maven POM.xml 标签详解
- 大数据和数据库的理解文章收藏
- 链接3: SQL语句教程
- 论文:Insights on Transfer Optimization: Because Experience is the Best Teacher(1)文章结构以及以及自己的感觉
- Python中的变量以及赋值语句
- [UI自动化]:控制浏览器操作
- iOS7系统iLEX RAT冬青鼠安装教程:无需刷机还原纯净越狱系统
- 如何进行Web服务的性能测试?
- AngularJs $cacheFactory 缓存服务