
模块原理的第1部分讲的是导入这一过程。在执行语句的时候, 它只会分成两个阶段, 第一个阶段是去搜索那个对应的模块, 第二个阶段则是把上面一步得到的搜索结果绑定到当前的局部命名空间里面。在进行搜索活动的时候, 需要分开成两个步骤来做。进行查找sys, 接着继续查找sys。当你导入一个模块的时候, 这个被导入的模块以及它里面去调用其他的那些模块的信息, 都会以一个字典的形式存放在_sys_变量里面, 如果后面再重复导入同一个模块的话, 那么就会优先去_sys_这个字典里查找有没有已经存在的模块, 你直接在脚本里去执行-print-sys_-这样的操作就可以看清楚当前到底加载了哪些模块的内容, 更进一步说, 我们实际上还可以手动去修改这个_sys_里面的数据。import osimport syssys.modules[fos] osimport fosprint(fos.getpid())当执行 fos 这个操作的时候, 程序会先去 sys 里面查找看看是否存在对应的模块情况, 这里使用的是 fos 作为 key, 也就是键值来进行查询操作, 一旦找到了相关的结果, 获得的 value 也就是值的部分是 os 模块对象本身, 正是因为找到了这一模块, 所以后续才能够正常去调用该模块里面的各种方法功能。如果在sys.模块中找不到目标模块则从sys.中继续寻找。sys.是一个list里面的对象是对象对象是指实现了 和 接口的对象输出sys.里的内容可以查看有什么这三个对象是分开进行搜索以及导入内置的模块, 或者说是那些已经被编译成了Unix能够执行的程序形式的模块, 也包含位于路径里面的模块, 在万一所有的情况当中都没有搜寻到对应的内容的时候, 系统就会抛出相应的错误信息。2. sys.path在启动导入模块的环节之中, 首先是先去到sys.里面进行查看工作, 如果发现没有查找到相关的内容的话, 那么接下来是会接着去使用sys.之中的项目来完成接下来的查找任务, 而这一系列的查找动作之中的首先一部分是会去寻找内置性质的模块内容, 随后会继续查找对应的模块内容, 最终才会完全依据sys.path内部所设定的那些具体路径来进行最后的查找操作。假如是在代码脚本文件之中直接运行打印这个叫做sys.path的内容的话, 于是便会在个人的电脑设备上面看到具体的输出结果为。/Users/kwsy/kwsy/coolpython/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/lib-dynload/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/proxypool-2.0.0-py3.6.egg/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/bs4-0.0.1-py3.6.egg/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/Flask_Mail-0.9.1-py3.6.egg/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/blinker-1.4-py3.6.egg在系统路径里边的这个路径的顺序, 它是决定了搜索的一个顺序的, 然后的话这里边提到的这些路径呢, 它总共是可以分为三大类的。sys.path表示当前的路径, 同时也是最先被搜索的目录, 此外第二类路径是在安装时内置进去的内容, 诸如类似///.//3.6/lib/.zip这样的文件。///.//3.6/lib/.6在路径///.//3.6/lib/.6/lib-这一地方。///.//3.6/lib/.6/site-最后一类就是安装的第三方模块如果模块存在于这些路径中, 那么, 不管它身在何处, 都是可以使用直接导入的的, 下面就是一个脚本的地址。/Users/kwsy/PycharmProjects/pythonclass/mytest/import_demo/conf/offline.py脚本内容为host192.168.0.2在其余的那些项目之中, 大家所采取的办法, 就是把那个后缀名为的文件所在的完整路径, 添加到系统的路径列表里面去, 这样一来, 就在编写的程序脚本里, 实现了能够直接进行代码引用的操作。import sysimport importlibsys.path.append(/Users/kwsy/PycharmProjects/pythonclass/mytest/import_demo/conf)import offlineprint(offline.host)module __import__(offline)print(module.host)module importlib.import_module(offline)print(module.host)在实践的环节中, 你的编辑器甚至会在这一个行里面显示出一个红色的波浪线, 这当然是在警告你找不到对应的模块这个情况。这种警告是由编辑器发送的, 原因在于.py所在的目录只在程序执行的期间才会被加入到sys.path之中。由于编辑器在你的编写代码这个阶段还检查不到sys.path中包含这一个目录, 因此编辑器找不到这个模块是正常现象。当程序开始运行时, 解释器却可以成功找到这个模块。这也属于是一种动态加载模块的技术范畴。3. hooks我们可以通过一些技术手段来对行为进行扩展, 为了让你有一个更加直观的理解, 这里推荐一个第三方的库pypi, 这个模块实现了一个非常神奇的功能, 该功能体现在你可以在代码里导入那些本来不存在的模块, 遗憾的是目前还不能够通过pip这个工具来安装这个模块, 你直接把pypi.py这个文件放在site-目录下面或者是直接放在项目里面, 关于该模块的git地址信息。此刻, 我们接着去做一个具体的实现操作。import pypiimport requestsprint(requests)我在执行这段代码的时候, 已经将对应的模块给卸载掉了, 但是在运行这段代码的过程中, 并没有抛出任何错误信息, 直到过了相当长的一段时间之后, 程序才顺利地继续往下走, 并且正常地执行了打印语句。关于 pypi.py 这个文件的源代码, 其复杂度其实是并不算高的。import importlib.abcimport importlib.machineryimport subprocessimport sysdef install(pkgname, versionNone):cmd [sys.executable, -m, pip, install]if version:cmd.append({}{}.format(pkgname, version))else:cmd.append(pkgname)subprocess.check_call(cmd,stdoutsubprocess.DEVNULL,stderrsubprocess.DEVNULL,)class PipFinder(importlib.abc.MetaPathFinder):def find_spec(self, fullname, path, targetNone):try:install(fullname)except subprocess.CalledProcessError:return Noneelse:return importlib.machinery.PathFinder().find_spec(fullname,path,target,)sys.meta_path.append(PipFinder())sys里面保存着对象, 如果在sys里找不着目标模块, 就用这上面的对象接着找, 在环境里头, 有人写了一个东西叫不到名字的玩意儿, 然后把它塞进sys里面, 专门拿来找指定模块的代码, 进到函数体内之后, 第一件事情就是调用安装函数来搞定模块这件事, 如果你猜的没错, 要是这个模块早就安好了, 那就啥反应也没有, 要是发现这模块以前压根没装过, 那就直接就给你装上。在安装操作完结之后, 接下来要执行动作来调用对应的模块, 具体的做法是加载预先设定好的那个模块。4. 有关的内容, 我们可以参考一下。