【魏先生搞定python系列】——模块、包以及库你所应该掌握的都在这里

    科技2022-07-13  133

    一直都知道,要想快速上手一门语言,最好的办法就是找个项目做,就好比武林高手闭门修炼,项目完工之日就是出关之时。之前一是因为懒(主要是因为懒),二是没有特别感兴趣的项目上手,所以就一直这么拖着。最近,考虑把自己关于电池性能预测的论文进行实现,算是有了一个像样的项目,目前程序框架已经搭建完毕,开始着手每个功能的实现。面对的第一个问题就是自己创建的包的调用问题,粗算下来,也花了我将近2个小时时间测试翻阅资料。现在记录下来,作为备忘,也用来摆渡有缘人。

    1. 模块、包及库的定义

    模块:就是.py文件,里面定义了一些函数和变量,需要的时候就可以导入这些模块。

    包:在模块之上的概念,为了方便管理而将文件进行打包。包目录下第一个文件便是 __init__.py,然后是一些模块文件和子目录,假如子目录中也有 __init__.py,那么它就是这个包的子包了。

    常见包结构如下:

    package_a ├── __init__.py ├──subpackage_1 

    ├──__init__.py

    └──module_a1.py └── module_a2.py

    说白了,包就是带有__init__.py文件的文件夹,这是它与普通文件夹的区别。如果再普通文件夹内新增了_init__.py文件,python也判定它是一个包。

    库:具有相关功能模块(包)的集合。这也是Python的一大特色之一,即具有强大的标准库、第三方库以及自定义模块。

    2. 模块、包的调用

    其实不管是模块还是包的调用,都遵循import的使用原则,万变不离其宗。

    2.1 模块的调用

    2.1.1 同一目录下的调用

    常见的同级目录下文件结构如下:

    其中,main_project为主程序入口;module_test1为要调用的模块,其内部定义了test1()函数。此时可以看到,调用文件与模块在同一目录下,此时在main_project调用方式为:

    import module_test1 module_test1.test1()

    2.1.2 不同目录下的调用

    假设模块与调用文件不在同一目录下,如下:

     此时,在main_project中,如果要调用module_test1模块,首先要将模块所在路径添加到import的搜索路径中(import的默认搜索路径只是标准库以及自己的目录),然后再对模块进行调用。

    这里给一个比较普适的例子,即通过os包的函数获取绝对路径,再通过sys包添加到搜索目录中:

    import os import sys absPath = os.path.abspath(__file__) #返回代码段所在的位置,肯定是在某个.py文件中 temPath = os.path.dirname(absPath) #往上返回一级目录,得到文件所在的路径 #temPath = os.path.dirname(temPath) #在往上返回一级,得到文件夹所在的路径 print(temPath) module_path = temPath+'\module_dir' print(module_path) sys.path.append(module_path) import module_test1 module_test1.test1()

    注意,这里设置的import搜索路径,必须正好在模块所在的目录,上一级或下一级目录都不行。通过添加搜索路径的方式,可将不在同级的模块做直接导入。

    当然,也不一定非得加入搜索路径,对于上面的例子,完全可以用from... import的形式进行导入,例如还是上面的结构,在main_project中写入,也可以通过.来写入:

    from module_dir import module_test1 module_test1.test1() #等价于 import module_dir.module_test1 module_dir.module_test1.test1()

    需要再次强调的是,对于添加搜索路径,因为是对系统路径的更改,因此变更一次,后续就都会后效。因此我再后面进行from...import测试,以及将主程序和模块放在不同层级文件夹中,都可以正常的进行import,算是彻底晕菜了。不过这反倒说明一个问题,修改搜索路径是把双刃剑,如果路径设置的多了,很有可能会出现模块名称重名,导致你想import的文件可能不是你想要的那个的情况。这也就体现出了包的重要性,通过使用包,可以很大程度避免模块重名的情况出现。

    2.2 包的调用

    对于包的调用,形式也有很多,其中可以像不同目录下模块调用一样,采用from 包名 import 模块名 或者 import 包名.模块名的形式来调用模块。但是我们在调用第三方库或者标准库的时候,可以直接import 包名,在程序中在具体说明调用哪个模块。但是在直接import自定义包的时候会出错,原因是__init__.py文件中要声明后才可调用。具体用法如下:

    上面是我们程序的结构,此时在__init__.py文件中添加:

    from . import package_module1

    这时就可以在main_project中实现单纯包的调用:

    import package_test package_test.package_module1.fuc2()

    在这里还要延伸探讨下,from . import 的格式,是使用相对路径导入模块,但是在使用时有可能出现attempted relative import with no known parent package的告警错误,这在参考文献[3]中有详细解读,这里暂不赘述。

    总的来说,模块、包和库的基础知识都在这里了,应该可以满足基本的需求。但是如果程序结构更复杂,可能还会出错。还是要从import的机理来下手理解。这里先附上参考文献[4],待后续阅读理解。

     

    参考文献

    [1] https://blog.csdn.net/qq_42451635/article/details/81913272

    [2] https://blog.csdn.net/u011797832/article/details/83002547

    [3] https://blog.csdn.net/nigelyq/article/details/78930330

    [4] https://blog.csdn.net/weixin_38256474/article/details/81228492

     

    Processed: 0.014, SQL: 8