暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

来!和我一起把 python 导入模块彻底搞懂

鸡仔说 2021-05-11
882

鸡仔说:代码的模块化,依赖于各个组件之间可以互相引用。不管你是 python 新手还是 pythoner 老师傅,或多或少都踩过包引入方面的坑。今天就和鸡仔一起探探底儿,看看 python 包、模块引入的猫腻。

包和模块

首先你应该知道包和模块的概念,简单的讲,模块,就是一个后缀为 .py 的文件。而包则是包含模块的任何文件夹(python2中,包是包含__init__.py的文件夹)。

import 是怎么工作的
当你引入一个模块或者包时,python 首先去 sys.modules 中去找,sys.modules 会缓存是之前引入过的所有模块,提高包导入的速度。如果该模块没有找到所需要的模块,则 python 会继续去系统内建库去找是否存在所需要的包。如果还没有找到,python 会继续从 sys.path 定义的目录表里面搜索,这个列表包含当前目录以及用户自己添加的工作目录。
当 python 找到相应的模块,就会给当前文件的作用域内绑定一个同名的变量,因此你才可以无障碍的使用你导入进来的模块。如果在以上三个地方都没有找到的话,就会抛出一个 ModuleNotFoundError
运作流程如下:

import 语法与规范使用
python 的包导入语法规则非常灵活,你可以引入一个包
import package
也可以引入一个模块
import module
还可以从一个包或者模块引入另一个包或者模块
from a.b.c import d, e, f
当然太灵活的副作用就是,如果不加以限制的随意引用,后期我们要花更多的时间整理和归档这些模块。好在 PEP 8 规范给了我们一些建议,遵照这些建议,会让我们的代码更规范易读
  1. 导入语句应该写在除注释和文档的最上面

  2. 应该将导入模块分成不同的三组

    1. 一组导入 python 内置的模块

    2. 一组导入第三方模块

    3. 余下的一组导入自己本地写的模块

  3. 组与组之间,应该用空行隔开

比如像下面这样
# coding: utf-8# @Time : 2021/5/9 6:15 PM# 系统内置库导入import abcimport mathimport reimport time# 第三方库导入import pymysqlimport requestsfrom loguru import logger# 本地模块导入from myproject.package.module import cal_somethingfrom myproject import util
绝对引入
我们现在假设有这样结构的程序,我们在项目的顶层目录下引入模块,用绝对引入的语法可以像这样写
import animalfrom animal import cow, monkeyfrom company.Internet.bat import tencent, alibaba, baidufrom food.fruit import apple
使用绝对引入的优势是,引入结果一目了然。未来如果要定位资源的路径,就非常容易找到。实际上,PEP 8 规范明确推荐使用绝对路径,因为理由也很简单:显示优于隐式。但也并不是说绝对引入没有弊端,唯一令人恼火的地方,就是如果你的项目层级很复杂,那么绝对引入就有可能变成这样
from universe.earth.China.Guangdong.Shenzhen import jizai
非常冗长对吧,并且,一旦动了其中某个模块的名字,那么涉及该模块的所有引用都需要一个个去改文件名。这对于经常持续重构来说,简直是灾难
相对引入
相对引入就是资源文件相对于当前文件的路径导入。引入语法如下
from .animal import cowfrom ..food.fruit import applefrom ...company.Internet.bat import tencent
好像和绝对路径区别不太明显,是吧?那我们看看最后那个例子,在同级目录下,你可以这样👇
from .Shenzhen import jizai
相对引入针对内嵌很深的项目结构,可以通过简洁的语法引入包和模块,但因为用隐式的方式表达,项目结构不易于阅读,后期的维护成本比较高。
github地址:https://github.com/hacksman/learn_lab/tree/master/small_bug_lab/module_import
总结
鸡仔研究 python 包导入问题的时候,看了些网上的开源项目,相对引入和绝对引入都有被用到开源项目中,比如 requests 库,就大量使用了相对引入,目录结构看起来非常简洁明了,scrapy 则使用了绝对引入。虽然相较于相对引入,导入部分的语法显得更加冗余,但却明确知道各个资源从哪里引入,非常让人安心。

requests 项目代码示例scrapy项目代码示例
最近鸡仔在写通用采集微服务,不同的微服务模块使用的是独立的文件夹,鸡仔采用的是__main__.py 作为启动文件的方式,外层通过 python -m package_name 的方式启动项目,也非常优雅,关于__main__.py 小伙伴们也可以自行在网上搜一下教程看下,善用它能够让你的外层启动程序更简洁,下次有机会再写文章分享出来。
祝春琪!
参考资料:

[1] Tom Tanner (2018). what is the difference between 'import a.b as b' and 'from a import b' in python [duplicate].

https://stackoverflow.com/questions/50943230/what-is-the-difference-between-import-a-b-as-b-and-from-a-import-b-in-python

[2] Mbithe Nzomo(2018). Absolute vs Relative Imports in Python.

https://realpython.com/absolute-vs-relative-python-imports/

[3] John Smith Optional. (2013). Relative imports in Python 3.

https://stackoverflow.com/questions/16981921/relative-imports-in-python-3

[4] 旷世的忧伤. (2017). Python 中的 if __name__ == '__main__' 该如何理解

https://blog.konghy.cn/2017/04/24/python-entry-program/



以上,如果觉得内容对你有所帮助,还请点个「在看」支持,谢谢各位dai佬!




好看的人都点了在看

文章转载自鸡仔说,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论