深入探讨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. 参考文档

◆
精彩推荐
◆

推荐阅读
相关文章:
吴恩达老师深度学习视频课笔记:人脸识别
什么是人脸识别:人脸验证和人脸识别的区别,如下图:One-shot learning:人脸识别所面临的挑战就是需要解决一次学习(one-shot learning)问题。这意味着在绝大多数人脸识别应用中你需要通过单单一张图像或者单单一个人脸图像就能去识…

用小白鼠喝毒药
题设:有N瓶水,其中有一瓶水有剧毒,如果小白鼠喝了会在24小时的时候死亡。 问:用多少只小白鼠能够检测出哪瓶水有剧毒? 要求:用的小白鼠数量少并且用时要短,并给出合理的过程与结论。 我的解题思…

怎样在swift中创建CocoaPods
本文由yake_099(博客)翻译自raywenderlich,作者:Joshua Greene 原文:How to Create CocoaPods with Swift 你可能对一些比较著名的开源的CocoaPods框架比较熟悉,比如Alamofire、MBProgressHUD。但是有时你…
吴恩达老师深度学习视频课笔记:神经风格迁移(neural style transfer)
什么是神经风格迁移(neural style transfer):如下图,Content为原始拍摄的图像,Style为一种风格图像。如果用Style来重新创造Content照片,神经风格迁移可以帮你生成Generated图像。深度卷积网络在学什么:如下图…

“Jupyter的杀手”:Netflix发布新开发工具Polynote
作者 | Michael Li 译者 | Rosie 编辑 | Jane 出品 | AI科技大本营(ID:rgznai100)【导读】10 月 29 日,Netflix 公开了他们内部开发的 Polynote。现如今,大型高科技公司公开其内部的工具或服务,然后受到业界…

System Center 2012 r2优点
System Center 2012System Center2012 是一个全面的管理平台,可帮助你轻松、高效地管理数据中心、客户端设备和混合云 IT 环境。为您提供了针对私有云、托管云和公有云基础结构和应用程序服务的通用管理工具集。可按照您的需求,为生产基础架构、可预期应…

Swift 闭包表达式
闭包是功能性自包含模块,可以在代码中被传递和使用。 Swift 中的闭包与 C 和 Objective-C 中的 blocks 以及其他一些编程语言中的 lambdas 比较相似。 闭包的形式主要有三种: 1. 全局函数是一个有名字但不会捕获任何值的闭包 2. 嵌套函数是一个有名字并可以捕获其封…

GNU AWK中BEGIN/END使用举例
以下是使用gnu awk将test.cpp文件拆分成两个文件a.cpp和b.cpp,其中b.cpp仅存放test.cpp中的数据,其它内容存放在a.cpp文件中。test.cpp内容如下: #include <stdio.h> #include <iostream> #include <string>int main() {//…

目标检测的渐进域自适应,优于最新SOTA方法
作者 | Han-Kai Hsu、Chun-Han Yao、Yi-Hsuan Tsai、Wei-Chih Hung、Hung-Yu Tseng、Maneesh Singh、Ming-Hsuan Yang译者 | 刘畅编辑 | Jane出品 | AI科技大本营(ID:rgznai100)【导读】目标检测的最新深度学习方法依赖于大量的边界框标注信息…

讨论下IDS的绕过
自从知道dedecms自带了80sec的内置Mysqlids后,一直以来也没有想到绕过的办法。或者是自己mysql的根底太差了吧。于是分析dedecms源码时,只找模板执行,本地包含,上传等,完全没有想到注入存在的可能性了。 可以看看某牛的…

GCC编译选项参数介绍
gcc和g分别是gnu的c和c编译器,下面是整理的常用编译选项参数: #(1). -x: 设定文件所使用的语言,使文件后缀名无效,如下,执行完后生成test.o gcc -c -x c test.jpg #(2). -c: 只编译生成目标文件即*.o,只编译不链接生成…

程序员自学到底有没有用?网友们吵翻了...
最近就有个程序员吐槽说,自己大学没怎么听老师讲课,老师讲的知识要么太旧,要么老师不会讲,自己大部分时间是在网上看视频学的。引发了以下激烈的讨论。很多网友觉得,学校老师的代码能力不行,现在技术更新又…

更新 FrameWork
这里把想要改变的东西封装到FrameWork以便实现热更新,提一下关于BundiD 一定要一致,在打包的时候一定在Edit scheme —— >Run 选择Release如图: 因为你要跑在真机上,所以这个要选择Release 另外将包含你想要放出的方法类添加…

把Illustrator矢量图转化为代码:Drawscript
2019独角兽企业重金招聘Python工程师标准>>> DrawScript是一款Illustrator插件,可以将Illustrator的矢量图片转换成代码,目前免费,支持转换的语言有 OBJ-CCJAVASCRIPTCREATEJS/EASELJSPROCESSINGACTIONSCRIPT 3JSONRAW BEZIER PO…

必读:ICLR 2020 的50篇推荐阅读论文
来源 | 香侬科技本文整理了ICLR2020的相关论文,此次分享的是从Openreview中选取的部分论文,共50篇,其中大部分为NLP相关。文中涉及的相关论文推荐指数与推荐理由仅为个人观点,利益无关,亦不代表香侬科技立场。希望大家…

14个Xcode中常用的快捷键操作
在Xcode 6中有许多快捷键的设定可以使得你的编程工作更为高效,对于在代码文件中快速导航、定位Bug以及新增应用特性都是极有效的。 当然,你戳进这篇文章的目的也在于想要快速的对代码文件进行操作,或者是让Xcode的各面板更为适应你小本子的屏…

C++中标准模板库std::pair的实现
以下用C实现了标准模板库中的std::pair实现,参考了 cplusplus 和 vs2013中的utility文件。关于std::pair的介绍和用法可以参考: https://blog.csdn.net/fengbingchun/article/details/52205149 实现代码pair.hpp如下: #ifndef FBC_STL_PAIR_H…

【人在职场】能力与价值
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://yunli.blog.51cto.com/831344/1547051 最近给团队(指#UC浏览器电脑版#开发团队)分享了我的《基层技术管理原则》。…
Windows与Linux之间互传文件的方法
以下方法均是以Windows为操作机:1. 通过WinSCP:WinSCP是一款开源的SFTP客户端,运行于Windows系统下,遵照GPL发布。WinSCP除了SFTP,还支持SSH、SCP(SecureCopy Protocol)。WinSCP的开发始于2000年4月,由布拉格经济大学所…

一文读懂简化的图卷积网络GCN(SGC)| ICML 2019
作者 | yyl424525来源 | CSDN博客文章目录1 相关介绍1.1 Simple Graph Convolution (SGC)提出的背景1.2 SGC效果2 Simple Graph Convolution 简化的图卷积2.1 符号定义2.2 图卷积网络GCNGCN vs MLPFeature propagation 特征传播Feature transformation and nonlinear transitio…

iOS UITableViewCell重用问题
TableView的重用机制,为了做到显示和数据分离,iOS tableView的实现并且不是为每个数据项创建一个tableCell。而是只创建屏幕可显示最大个数的cell,然后重复使用这些cell,对cell做单独的显示配置,来达到既不影响显示效果…

NLP常用工具
为什么80%的码农都做不了架构师?>>> NLP常用工具 各种工具包的有效利用可以使研究者事半功倍。 以下是NLP版版友们提供整理的NLP研究工具包。 同时欢迎大家提供更多更好用的工具包,造福国内的NLP研究。 *NLP Toolbox CLT http://compl…

Swift快速入门之getter 和 setter
属性可以用getter和setter方法的形式提供。 <code class"hljs lasso has-numbering" style"display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: Source Code Pro, monospace;font-size:u…

Linux下getopt函数的使用
getopt为解析命令行参数函数,它是Linux C库函数。使用此函数需要包含系统头文件unistd.h。 getopt函数声明如下: int getopt(int argc, char * const argv[], const char * optstring); 其中函数的argc和argv参数通常直接从main的参数直接传递而来。o…

20行Python代码说清“量子霸权”
作者 | 马超 来源 | 程序人生(ID:coder_life)近日谷歌的有关量子霸权(Quantum Supremacy)的论文登上了Nature杂志150年刊的封面位置,而再次罢占各大媒体的头条位置,其实这篇文章之前曾经短暂上过NASA的网站…

Android组件系列----BroadcastReceiver广播接收器
【声明】 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/3960623.html 【正文】 一、广播的功能和特征 广播的生命周期很短,经过调用对象-->…

Swift 代码调试-善用XCode工具(UI调试,五种断点,预览UIImage...)
原创Blog,转载请注明出处 http://blog.csdn.net/hello_hwc?viewmodelist 我的stackoverflow 工欲善其事,必先利其器,强烈建议新手同学好好研究下XCode这个工具。比如Build Settings,Build Info Rules,Build Parse…

Linux下getopt_long函数的使用
getopt_long为解析命令行参数函数,它是Linux C库函数。使用此函数需要包含系统头文件getopt.h。 getopt_long函数声明如下: int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex);…

Expect自动化控制简单介绍
telnet,ftp,Passwd,fsck,rlogin,tip,ssh等等。该工具利用Unix伪终端包装其子进程,允许任意程序通过终端接入进行自动化控制;也可利用Tk工具,将交互程序包装在X11的图形用…

C++中标准模板库std::vector的实现
以下实现了C标准模板库std::vector的部分实现,参考了 cplusplus. 关于C中标准模板库std::vector的介绍和用法可以参考 https://blog.csdn.net/fengbingchun/article/details/51510916 实现代码vector.hpp内容如下: #ifndef FBC_STL_VECTOR_HPP_ #defi…