Python中的函数(Function)、类(Class)、模块(Module)、包库(Package),都是为了实现模块化引用,让程序的组织更清晰有条理。
- 通常,函数、变量、类存储在被称为模块(Module)的.py文件中,一组模块文件又组成了包(Package)。
- 将函数、变量、类存储在存储在独立的.py文件中,可隐藏代码实现的细节,将不同代码块重新组织,与主程序分离,简化主程序的逻辑,提高主程序的可读性。
- 有了包和模块文件,可以在其他不同程序中进行复用,还可以使用其他人开发的第三方依赖库。
- 便于协同开发,不同人员独立开发各自的包,最后形成项目。
1. Packages官方解释
1.1 包(package)与模块(module)
python 官方对package的解释如下。官方地址:https://docs.python.org/zh-cn/3/tutorial/modules.html#packages
Packages 可以理解为一组模块的包,并用Package.Module的方式来构建命名空间。文件夹中必须有__init__.py
这个文件,才能使 Python 将包含该文件的目录视为包(Package)。
例如,这是一个官方的package例子,提供了关于声音处理的sound
包:
sound/ Top-level package__init__.py Initialize the sound packageformats/ Subpackage for file format conversions__init__.pywavread.pywavwrite.pyaiffread.pyaiffwrite.pyauread.pyauwrite.py...effects/ Subpackage for sound effects__init__.pyecho.pysurround.pyreverse.py...filters/ Subpackage for filters__init__.pyequalizer.pyvocoder.pykaraoke.py...
__init__.py
必须有这个文件,才能使 Python 将包含该文件的目录视为包(Package)。__init__.py
可以是一个空文件,也可以执行包的初始化代码或设置__all__
变量。__all__指定的是指此包被import * 的时候, 哪些模块会被import进来.- formats/ 、effects/ 、filters/ 是次一级的子包(Subpackage),每个子包中也有
__init__.py
文件。 - echo.py等文件是子包中的模块(Module),模块中可能包含函数、类或变量。
引用包(package)中的模块(modules)的示例:
import sound.effects.echo
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
引用包(package)中子包(submodule)的示例:
from sound.effects import echo
echo.echofilter(input, output, delay=0.7, atten=4)
直接引用包(package)中的函数或变量的示例:
from sound.effects.echo import echofilter
echofilter(input, output, delay=0.7, atten=4)
1.2 使用__all__提供包的显示索引
当我们直接采用from sound.effects import *
时,可能会引用一些不需要的内容,或者导致加载速度过慢。 因此import 语句使用以下约定:包的__init__.py
代码可以定义了一个名为__all__
的列表,来指定用 *
时应导入的模块名称列表。假如sound/effects/__init__.py
包含以下代码:
__all__ = ["echo", "surround", "reverse"
]
这意味着from sound.effects import *
将会加载这3个字模块。
需要注意的是,子模块可能会被本地定义的函数名遮挡,如果您在sound/effects/__init__.py
文件中添加了一个reverse
函数,则from sound.effects import *
将只导入两个子模块echo
和surround
,而不导入reverse
子模块,因为本地定义了reverse
函数名:
__all__ = ["echo", # refers to the 'echo.py' file"surround", # refers to the 'surround.py' file"reverse", # !!! refers to the 'reverse' function now !!!
]def reverse(msg: str): # <-- this name shadows the 'reverse.py' submodulereturn msg[::-1] # in the case of a 'from sound.effects import *'
1.3 包内引用
构造成的子包(如示例中的sound
包),可以使用绝对导入来引用兄弟包的子模块。例如,如果模块sound.filter.vocoder
需要使用sound.effects
包中的echo
模块,则可以使用 from sound.effects import echo
。
您还可以使用import
语句的from module import name
形式相对导入。这些导入使用.
来指示相对导入中涉及的当前包和父包。例如,从surround
模块中,可以使用:
from . import echo
from .. import formats
from ..filters import equalizer
请注意,相对导入基于当前模块的名称。由于主模块的名称始终为__main__
,因此打算用作Python应用程序主模块的模块必须始终使用绝对导入。
2. 示例demo
创建以下目录结构文件:
package-test
|- __init__.py
|- package_1
|-- subpackage_1
|--- __init__.py
|--- module_1.py
|--- module_2.py
module_1.py
:
def func_1():print('func_1')
module_2.py
:
def func_2():print('func_2')
子包subpackage_1
中定义了__init__.py
,包含了module_1
及module_2
两个子模块:
from . import module_1
from . import module_2 # 注:每个模块占一行,方便Git代码比对__all__ = ['module_1', # 注:每个模块占一行,方便Git代码比对'module_2',
]
包package_1
中定义了__init__.py
,包含了subpackage_1
子包:
from . import subpackage_1__all__ = ['subpackage_1',
]
主函数main.py
可调用pakcage_1
包的内容:
from package_1 import subpackage_1if __name__ == '__main__':subpackage_1.module_1.func_1()subpackage_1.module_2.func_2()