python 中的import、模块和包

包和模块、import

简单的说,Python 中一个 以 .py 结尾的 Python 文件,就是一个模块(Module),包含了 Python 对象定义和 Python 语句。
包就是文件夹,但该文件夹下必须存在 __init__.py 文件, 该文件的内容可以为空。__init__.py 用于标识当前文件夹是一个包。

模块

Python 中一个 以 .py 结尾的 Python 文件,就是一个模块(Module),包含了 Python 对象定义和 Python 语句。模块能够让你有逻辑地组织 Python 代码段。把相关的代码分配到一个模块里能让代码更好用,更易懂。模块里既能定义函数,类和变量,也能包含可执行的代码。

模块的引入

当解释器遇到 import 语句,如果模块在当前的搜索路径就会被导入。搜索路径是一个解释器会先进行搜索的所有目录的列表。不过,不管你执行了多少次 import,一个模块只会被导入一次(第一次导入后就将模块名加载到内存了,后续的 import 语句仅是对已经加载大内存中的模块对象增加了一次引用,不会重新执行模块内的语句)。这样可以防止导入模块被一遍又一遍地执行。如果你想重新执行模块里顶层部分的代码,可以用 reload(modul_name) 函数。该函数会重新导入之前导入过的模块。Python 3 移动到了模块 importlib 中。

import ... 语句
from ... import ... 语句:Python 的 from 语句让你从模块中导入一个指定的部分到当前命名空间中。小心与自定义的对象重名,导致被覆盖掉了。
from ... import * 语句:导入模块中定义的全部公有名称。这种声明不要过多地使用,可读性极差,而且导入了过多不必要的对象。不过,有时会与 __all__ 一起使用,此时,会一次性导入 __all__ 变量中列出的所有模块。

PEP8 建议:

1.py 文件中导入模块的顺序:1)Python内置模块;2)第三方模块;3)自定义模块;且应在每个导入组之间放一行空行。
2.模块级别的内置属性(名字有前后双下划线的),例如 __all__, __author__, __version__,应该放置在模块的文档字符串后,任意 import 语句之前,from __future__导入除外。Python强制要求from __future__导入必须在任何代码之前,只能在模块级文档字符串之后。

from __future__ import barry_as_FLUFL

__all__ = ['a', 'b', 'c']
__version__ = '0.1 '
__author__ = 'Cardinal Biggles'

import os
import sys
搜索路径

当你导入一个模块,Python 解析器对模块位置的搜索顺序是:
1、当前目录;
2、如果不在当前目录,Python 则搜索在 shell 变量 PYTHONPATH 下的每个目录
3、如果都找不到,Python 会查看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/。

模块搜索路径存储在 system 模块的 sys.path (list,指明了所有查找 module,package 的路径)变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。

包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的 Python 的应用环境。
简单的说,包就是文件夹,但该文件夹下必须存在 __init__.py 文件, 该文件的内容可以为空,也可以有 Python 代码,原则是尽量保持 __init__.py 文件的精简。__init__.py 用于标识当前文件夹是一个包。若这个文件不存在,那么这个目录只是一个目录而不是一个包。有如下目录的 m3.py、test1.py

├─root                               
│  ├─pack1
│  │  ├─pack2
│  │  │  ├─pack3
│  │  │  │  ├─__init__.py 
│  │  │  │  ├─m3.py
│  │  ├─__init__.py
│  │  ├─m2.py
│  ├─test1.py

# m3.py
def pack3_print():
    print("我是m3!")

pack_3 中的 __init__.py 文件中为空,m3.py 有一个打印函数 pack3_print()。由于调用包的其他模块所在的绝对路径是千变万化的,所以在包的内部调用自身其他文件中的函数、常量、类,应该使用相对路径,而不是绝对路径。

1.现在若要在 test1.py 文件夹调用这个函数,可以这么做:

1)按照路径导入即可:

from root.pack1.pack2.pack3.m3 import pack3_print

pack3_print()

or

import pack1.pack2.pack3.m3

pack1.pack2.pack3.m3.pack3_print()

2)在 test1.py 同级目录的 pack1 的文件夹下创建一个 __init__.py 文件, 文件内容是:

from .pack2.pack3.m3 import pack3_print

再在 test1.py 中引入 pack3_print 函数:

from pack1 import pack3_print

pack3_print()

from . import <some-module> 句法表示相对导入。语句中的 . 表示当前包。还有 from .. import <some-module> ,这里的 .. 表示当前包的上一层。
包内部的文件之间互相导入可以使用相对导入,并且通过提前把函数、常量、类导入到 __init__.py 中后,包外面再想导入这些内容时,就可以直接用 from 包名 import 函数名 导入了,可以精简代码。

2.我们再在 pack2 下创建一个 m2.py 文件,在 m2 中调用 pack3_print 函数:

# 在 m2.py 使用相对路径直接导入:
from .pack2.pack3.m3 import pack3_print

def pack2_print():
    print('我是m2!')
    pack3_print()

不过,由于前面我们已经在 __init__.py 导入过 pack3_print 函数了,所以,我们可以在 m2 中这样直接导入:

from . import pack3_print -- 注意一下循环导入依赖的问题?

我们再修改上面的 __init__.py,把 pack2_print 函数导入进去:

from .pack2.pack3.m3 import pack3_print
from .m2 import pack2_print

最后,在 test1.py 中调用这个 pack2_print 函数:

from pack1 import pack3_print
from pack1 import pack2_print

pack3_print()
pack2_print()
posted @ 2022-04-20 20:15  海天之涯  阅读(947)  评论(0编辑  收藏  举报
回到顶部