python中的import(涉及pkgutil和inspect包)


python中万物皆对象。维度比较大的有模块、包。

一个.py文件就是一个python模块(module),如果一个目录下面有一个__init__.py文件,那么这个目录就是一个python包(package)。

当然,这只是极简版的概念。实际上包是一种特殊的模块,而任何定义了__path__
属性的模块都被当做包。

以两个下划线开头,以两个下划线结尾的属性,暂称魔法属性(自创的),对应的有魔法方法。——用特别的格式表示能实现一些特别的功能。

在python代码中使用模块或包,需要使用import语句。

有两种import方法:relative import和absolute import。

相对导入,是通过指出相对当前目录位置的偏移来导入对应目录下的模块。

绝对导入,就是直接指出哪个包,哪个模块。

当python解释器看到import语句后,要做两件事:找到module,将module加入到local namespace中。

具体就是,根据import的指示,去寻找(find_module)到对应的module,并导入(load_module)了。

在第一步查找时,遵循:

  1. 检查 sys.modules (保存了之前import的类库的缓存),如果module被找到,则⾛到第二步。
  1. 检查 sys.meta_pathmeta_path 是一个 list,⾥面保存着一些 finder 对象,如果找到该module的话,就会返回一个finder对象。
  2. 检查⼀些隐式的finder对象,不同的python实现有不同的隐式finder,但是都会有 sys.path_hooks, sys.path_importer_cache 以及sys.path
  3. 抛出 ImportError

详细内容可以参考:
Python Import 机制与拓展
Python 的 import 机制

这里说几个典型应用吧。

判断模块是否已导入
若已导入,必定在sys.modules中。sys.modules是已导入模块的字典,key是模块名,value是模块对象。

获取某已导入模块的所有属性
dir(sys.modules[module_name])获取对应key==module_namevalue值。
help(dir)的输出:

Help on built-in function dir in module __builtin__:

dir(...)
    dir([object]) -> list of strings

    If called without an argument, return the names in the current scope.
    Else, return an alphabetized list of names comprising (some of) the attributes
    of the given object, and of attributes reachable from it.
    If the object supplies a method named __dir__, it will be used; otherwise
    the default dir() logic is used and returns:
      for a module object: the module's attributes.
      for a class object:  its attributes, and recursively the attributes
        of its bases.
      for any other object: its attributes, its class's attributes, and
        recursively the attributes of its class's base classes.

在其中提到了,可以使用__dir__魔法方法来自定义。

之后在inspect或是需要了解对象内部的情况时,还会用到它。

为什么会报ImportError,如何规定导入路径
sys.path 是当前环境的module搜索路径,如果想要找到某package,就需要将此package的目录加入到这个list当中,也就是对应package中的__init__.py文件所在的目录。

如使用pip安装的包就位于'/Library/Python/2.7/site-packages'目录。

通常,在这个列表中,会加入当前目录。

一个关于Class A和Class B递归导入的经典问题
import 迷宫

sys.meta_path和其他path hook的区别
处理sys.path时会使用sys.path_hooks,它会顺序地检查sys.path中的每一项,如不能处理则抛出ImportError,如果可以返回一个importer对象,并返回。
sys.meta_path见下。

自定义导入,写一个import hook
可以通过import hook,来在模块导入时做一些工作。

我们已经知道,导入模块首先是要进行查找。而查找的第一步是检查sys.modules,第二步是检查sys.meta_path,使用其中的finder对象来查找所需要的module。
正常情况下,sys.meta_path是一个空列表。

finder对象必须实现find_module()方法,它可以找到模块,并返回一个load_module()方法。
loader对象负责加载模块,必须实现load_module()方法。
importer对象,实现了find_module()load_module()方法,即在实际中,只需要实现一个importer类,此类有find_module()load_module()两个方法。

具体可见上面引用的第一篇blog。

函数、类属于哪个模块
在一个文件中,可以通过from module_name import function_name或者from module_name import class_name,来导入其他模块的函数,或类。

在这种情况下,会发现这些导入的对象,也存在于该模块的dir输出中。

可以通过这些对象的__module__魔法属性来判断是在本模块中定义,还是从外部导入。

使用这个方法,也可以得知某一方法是与类属于同一模块,还是继承自基类(我并没有去验证基类和子类属于同一模块的情况,这种情况应该比较罕见吧?)。

相对导入和绝对导入

绝对导入就是明确指出是从哪个包或模块导入。

相对导入是指相对于目前模块所处的文件位置,来找到某个模块来导入。如from . import xxx是从当前模块所在的文件的同层目录中寻找,一个点表示同层目录,两个点表示上一层目录,以此类推。

相对导入会有一些麻烦。推荐使用绝对导入,这也是python推荐的。通过设置sys.path来将路径加入,这样在import时,python会到设置的路径中自动匹配。

pkgutil

最常用的有两个:

iter_modules(path=None, prefix='')
Yields (module_loader, name, ispkg) for all submodules on path, or, if path is None, all top-level modules on sys.path.
path是包的目录路径,prefix是输出时,所有包的名字的前缀。用来获取该path下的子模块或子包。

walk_packages(path=None, prefix='', onerror=None)
Yields (module_loader, name, ispkg) for all modules recursively on path, or, if path is None, all accessible modules.
同上,但是这个方法是递归获取路径下的所有模块。

inspect

常同pkgutil结合用。

getmembers(object, predicate=None)
返回object的所有属性,即dir中的各项。
这些属性各种各样,通过设置predicate来进行筛选,如当predicate=ismethod,只有当其为类的方法时,才会被选中。

getdoc(object)
获取对象的docstring,即__doc__

getargspec(func)
获取函数对象的arg。

还有很多其他方法,详见inspect的帮助文档。


最后的TODO

最近在做一个自动化测试的项目,第一步就是要找到所有的模块、类、方法、函数,以上就是涉及到的一部分基础知识。

过程中,发现dir输出的内容要远远多于真实在代码里写出来的东西,对于方法或属性,也需要去了解一下。

Python 魔术方法指南可以起到一定的帮助作用,剩下的还需要继续搜索。


其他相关博文:
python非侵入式代码监控(一): python import hook
PEP 302 -- New Import Hooks
PEP 369 -- Post import hooks
Python源码剖析笔记5-模块机制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • 模块和包 一 模块 1 什么是模块? 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是...
    go以恒阅读 2,226评论 0 4
  • 当前目录 和 脚本目录 参考资料:https://techibee.com/python/get-current-...
    ThomasYoungK阅读 10,238评论 0 11
  • If you quit from the Python interpreter and enter it agai...
    linyk3阅读 334评论 0 0
  • IO密集型程序、深拷贝和浅拷贝、模块导入、with 语句 1.1 GIL 学习目标 1. 能够说出 GIL 是什...
    Cestine阅读 611评论 0 0
  • [TOC] 最开始写程序的时候,都是一个文件里输入几行源码(python 的一个 web 框架bottle就特别强...
    人世间阅读 5,316评论 1 10