python中使用import来将别的模块导入到自己的脚本里面使用,那么什么样的文件才能被识别为模块,又该如何制作自己的模块呢。这一切都得从__init__.py这个文件说起。

我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。

什么是模块?

模块(module),就是一组共同实现一小块功能的python文件集合。这些文件被放到一个文件夹下,文件夹的名字就是模块名。通过import方法可以从模块中导入部分或者全部的文件或者是其中定义的类以及方法。

看一下官方的json模块的文件结构

(file) [fuhx@testmachine json]$ tree
.
├── decoder.py
├── encoder.py
├── __init__.py
├── scanner.py
└── tool.py

0 directories, 5 files

可能还会多一个__pycache__的目录出来,里面包含解释器编译的字节码,以pyc或者pyo后缀结尾。如果下次再次运行该模块的时候检查发现模块没有任何改变,就直接跑字节码,否则就重新编译

可以看到这里有5个文件,其中decoder.pyencoder.pyscanner.py 以及tool.py里面都是具体的实现代码,共同去组成json这么一个模块所需要完成的各种功能。

那这个多出来的__init__.py又是干啥的呢?

__init__.py文件

简化导入

首先是其第一个功能,简化导入。

例如下面的这个目录结构

(file) [fuhx@testmachine module1]$ tree
.
├── folder1
│   ├── file1_1.py
│   └── file1_2.py
├── folder2
│   ├── file2_1.py
│   ├── file2_2.py
│   └── __init__.py
└── test.py

2 directories, 6 files

其中file1_1.pyfile2_1.py中的内容如下

# file1_1.py
def func1_1():
    print(__file__)
# file2_1.py
def func2_1():
    print(__file__)

用来测试的test.py的内容如下

from folder1 import file1_1
from folder2 import file2_1

file1_1.func1_1()
file2_1.func2_1()

运行的结果如下

/home/fuhx/python_projects/file/module1/folder1/file1_1.py
/home/fuhx/python_projects/file/module1/folder2/file2_1.py

一切看起来很美好。

但是假如folder1folder2里面的文件过多,不可能一个一个去导入。如果能直接用我们熟悉的import folder1或者是from folder2 import *这样子就把所有的文件导入该多好。

可惜目前还做不到,需要__init__.py文件来帮忙了。

拥有__init__.py文件的目录,在被import的时候会自动执行该文件的内容,编辑folder2/__init__.py如下

from folder2 import file2_1
print("Hello init")

注意,在test.py中执行import操作的时候,是从test.py的目录执行,所以在这里不能直接import file2_1而是要有完整路径

之后修改test.py内容如下

import folder2

folder2.file2_1.func2_1()

能够成功返回结果。

Hello init
/home/fuhx/python_projects/file/module1/folder2/file2_1.py

像这样我们可以在__init__.py文件中将本模块的所有文件都导入,在外部直接整体导入模块就相当于导入了其中定义的所有文件。

筛选导入内容

同时,__init__.py文件还具有筛选导入内容的功能。没有在__init__.py中显式声明的内容就不会被外部使用,例如folder2/file2_2.py这个文件,使用的话会报错

AttributeError: module 'folder2' has no attribute 'file2_2'

__all__变量

筛选导入内容是我们所必须的,但是在里面一行行去声明文件又太繁琐,于是引入了__all__这样一个变量。对该变量传递一个list,里面声明要导入的文件名即可。

例如

__all__ = ['file2_1']

注意省略文件后缀

之后就可以用*号去导入了

from folder2 import *


file2_1.func2_1()

成功返回结果

/home/fuhx/python_projects/file/module1/folder2/file2_1.py

而没有在__all__变量中的文件同样不能被访问。

但是这样子就需要记住folder2被导入的内容了,或者是在模块的帮助文档中说明清除了。

初始化操作

当然,除了使得模块导入更加简洁和自由之外,因为其中的内容在模块导入阶段就会被执行,所以还可以在里面执行一些初始化操作。

其中最值得一提的就是django中连接mysql是对pymysql的伪装

import pymysql

pymysql.install_as_MySQLdb()

可以参考我的django专栏中的《【Django 007】数据模型models数据库交互详解》

在json库中的__init__.py文件中也进行了很多初始化操作,感兴趣的朋友也可以去看看。

制作自己的模块

有的时候还会出现存在__init__.py文件,该导入的也都导入了,但是还是提示找不到模块是咋回事呢?

这可能就是没有把该模块加入到path中的缘故。

类似于linux中的环境变量PATH,python中也用sys.path来保存用来搜索模块的目录列表。只需要将模块放入其中任意一个目录就可以被解释器搜寻到了。

说了这么多,基本就可以总结下如果要制作一个自己的模块的操作了

  • 将所有的脚本组织在一个大的文件夹下,文件夹名字为模块名

  • 将其中供外界调用的接口在__init__.py中进行导入操作,同时完成初始化操作

  • 将该模块加入到python的path中供解释器使用

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐