Python 包,常规包,命名空间包介绍
关注 1800
本节所讲述的内容,并不完全适用于通过类ModuleType动态创建的表示包的模块类型对象。
Python 中的模块和包有何不同?如何在代码中区别模块、常规包、命令空间包视频演示 YouTubePython 中的模块和包有何不同?如何在代码中区别模块、常规包、命令空间包视频演示 Bilibili
Python 包
Python 包是一类特殊的模块,他并不对应同名的py脚本文件,而是对应同名的文件夹。
如何判断模块是否为包?
在代码层面,如果一个模块具有__path__特性,则该模块为包。__path__特性可用于指示包对应的文件夹路径。
在交互模式中,我们尝试查看模块re和os的__path__特性。从输出结果可以看出,模块re是包,模块os不是包。
import re
re.__path__
['…\\python312.zip\\re']
import os
os.__path__
…
AttributeError: module 'os' has no attribute '__path__'. Did you mean: '__all__'?import re
re.__path__
['/usr/lib/python3.11/re']
import os
os.__path__
…
AttributeError: module 'os' has no attribute '__path__'. Did you mean: '__all__'?如何命名包?
包的名称默认与其对应的文件夹名称相同,并不需要特意的命名。比如,一个名称为workers的文件夹,其对应的包名称为workers。
应该称呼模块还是包?
由于包是一种特殊的模块,因此,称呼某个包为模块并不算错误,比如,re模块。
模块
如果你需要了解 Python 模块,可以查看Python 模块完全限定名,模块缓存介绍一节。
Python 常规包
Python 常规包对应的文件夹,拥有一个名为__init__.py的文件,他相当于模块对应的同名py文件,包含包的相关代码。
常规包的存储形式
在编写代码期间,常规包存储为文件夹和文件夹中__init__.py文件。但编译后,常规包的相关代码会转变为字节码并存储在pyc文件中。
这里,包hero对应的文件夹包含__init__.py,因此hero是一个常规包。
# hero 是一个常规包
print('我是常规包 hero!')Python 命名空间包
如果包对应的文件夹未包含名为__init__.py的文件,则该包被称为命名空间包。
命名空间包和常规包之间的区别
事实上,常规包和命名空间包都具有命名空间的效果,在不同的常规包或命名空间包中,你可以定义名称相同的内容。至于区别,除了是否包含文件__init__.py,还有以下几点。
命名空间包的__file__特性为None,而常规包为__init__.py文件的路径。命名空间包的__path__特性为_NamespacePath类型,而常规包为列表。命名空间包的__loader__特性为NamespaceLoader类型,而常规包为SourceFileLoader类型。
模块的 __loader__ 特性
模块的__loader__特性表示用于加载该模块的加载器。
这里,我们新建一个空的文件夹enemies,他将对应命名空间包。切换命令行至enemies的上级文件夹,启动 Python 交互模式并使用import进行导入操作,然后查看enemies包的相关信息。
import enemies
enemies.__file__ == None
True
enemies.__path__
_NamespacePath(['…\\packages\\enemies'])
enemies.__loader__
<_frozen_importlib_external.NamespaceLoader object at …>import enemies
enemies.__file__ == None
True
enemies.__path__
_NamespacePath(['…/packages/enemies'])
enemies.__loader__
<_frozen_importlib_external.NamespaceLoader object at …>命名空间,作用域
要了解什么是命名空间,可以查看编程教程的命名空间,作用域介绍一节。
zip 文件中的 Python 包
在zip文件中,包只能以常规包的形式存在,他们应该具有脚本文件__init__.py,否则将无法被导入。
在下面的示例中,压缩文件plants.zip含有常规包flowers,与plants.zip处于同一目录的脚本文件my_plants.py,会尝试导入zip中的flowers包。启动命令行并切换至文件my_plants.py所在的目录,运行后可以看到相关的输出结果。
# flowers 是一个常规包
print('我是包 flowers!')# 获取压缩文件 plants.zip 的绝对路径,并添加至模块搜索路径
import os
import sys
zip_path = os.path.abspath('plants.zip')
sys.path.append(zip_path)
# 导入 plants.zip 中的 flowers 包
import flowers我是包 flowers!如果将plants.zip中的脚本文件__init__.py删除,那么重复上述执行步骤会引发异常ModuleNotFoundError。
Python 子模块和子包
包对应的文件夹中的py文件或子文件夹,即为该包的子模块和子包,子包可以是常规包,也可以是命名空间包。
Python 模块和包的执行优先级
虽然这并不符合规范,但处于同一位置的模块和包可以具有相同的名称。在这种情况下,导入操作将按照如下优先顺序进行,常规包的优先级最高,模块次之,命名空间包的优先级最低。当同名模块或包中的一个被导入后,另一个将被忽略。
在包school中,存在子包homework和子模块homework,其中子包homework是一个常规包。在脚本文件my_school.py(与school包对应的文件夹处于同一目录)中,表达式import school.homework导入的是常规包homework,而不是模块homework。
print('功课太多了!')print('这里是模块 homework!')# 导入包 school 的子包 homework,他是一个常规包
import school.homework功课太多了!如果把文件夹homework中的脚本文件__init__.py删除,那么常规包homework会变为命名空间包,再次运行my_school.py将导入模块homework。
这里是模块 homework!内容分类
源码
hero/__init__.py·codebeatme/python-reference·GitHub
plants/flowers/__init__.py·codebeatme/python-reference·GitHub
my_plants.py·codebeatme/python-reference·GitHub
school/homework/__init__.py·codebeatme/python-reference·GitHub
school/homework.py·codebeatme/python-reference·GitHub
my_school.py·codebeatme/python-reference·GitHub
讲解视频
Python 中的模块和包有何不同?如何在代码中区别模块、常规包、命令空间包·YouTubePython 中的模块和包有何不同?如何在代码中区别模块、常规包、命令空间包·Bilibili