Python 模块和 Python 包有什么区别?
另请参阅:What's the difference between "package" and "module"(适用于其他语言)
__init__.py
文件。
from plumbum.cmd import ls
implementation
__init__.py
__init__.py
文件以被视为包。 @GiulioPiancastelli。请参阅PEP 420 -- Implicit Namespace Packages
任何 Python 文件都是 module,它的名称是文件的基本名称,不带 .py
扩展名。 package 是 Python 模块的集合:虽然模块是单个 Python 文件,但包是 Python 模块的目录,其中包含一个额外的 __init__.py
文件,以将包与恰好包含一堆文件的目录区分开来Python 脚本。包可以嵌套到任何深度,前提是相应的目录包含它们自己的 __init__.py
文件。
模块和包之间的区别似乎只存在于文件系统级别。导入模块或包时,Python 创建的相应对象始终为 module
类型。但是请注意,当您导入包时,只有该包的 __init__.py
文件中的变量/函数/类是直接可见的,不 子包或模块。例如,考虑 Python 标准库中的 xml
包:它的 xml
目录包含一个 __init__.py
文件和四个子目录;子目录 etree
包含一个 __init__.py
文件,以及一个 ElementTree.py
文件。看看当您尝试以交互方式导入包/模块时会发生什么:
>>> import xml
>>> type(xml)
<type 'module'>
>>> xml.etree.ElementTree
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'etree'
>>> import xml.etree
>>> type(xml.etree)
<type 'module'>
>>> xml.etree.ElementTree
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'ElementTree'
>>> import xml.etree.ElementTree
>>> type(xml.etree.ElementTree)
<type 'module'>
>>> xml.etree.ElementTree.parse
<function parse at 0x00B135B0>
在 Python 中,还有一些内置模块,例如 sys
,它们是用 C 编写的,但我认为您不打算在您的问题中考虑这些。
模块是在一次导入下导入并使用的单个文件(或多个文件)。例如
import my_module
包是目录中提供包层次结构的模块的集合。
from my_package.timing.danger.internets import function_of_love
__path__
属性的模块。
__init__.py
文件的组合形成。它们是可以包含其他模块的模块。
__path__
属性的 Python 模块。
首先,请记住,在其精确定义中,模块 是 Python 解释器内存中的一个对象,通常通过从磁盘读取一个或多个文件来创建。虽然我们可能非正式地将磁盘文件(例如 a/b/c.py
)称为“模块”,但它实际上并不会成为一个,直到它与来自其他几个来源(例如 sys.path
)的信息相结合以创建模块对象。
(请注意,例如,可以从同一个文件中加载具有不同名称的两个模块,具体取决于 sys.path
和其他设置。这正是解释器中 python -m my.module
后跟 import my.module
所发生的情况;将是两个模块对象 __main__
和 my.module
,它们都是从磁盘上的同一文件 my/module.py
创建的。)
包是可能具有子模块(包括子包)的模块。并非所有模块都可以做到这一点。例如,创建一个小的模块层次结构:
$ mkdir -p a/b
$ touch a/b/c.py
确保 a
下没有其他文件。启动 Python 3.4 或更高版本的解释器(例如,使用 python3 -i
)并检查以下语句的结果:
import a
a ⇒ <module 'a' (namespace)>
a.b ⇒ AttributeError: module 'a' has no attribute 'b'
import a.b.c
a.b ⇒ <module 'a.b' (namespace)>
a.b.c ⇒ <module 'a.b.c' from '/home/cjs/a/b/c.py'>
模块 a
和 a.b
是包(实际上,是一种称为“命名空间包”的包,尽管我们在这里不必担心)。但是,模块 a.b.c
不是一个包。我们可以通过在上面的目录结构中添加另一个文件 a/b.py
并启动一个新的解释器来证明这一点:
import a.b.c
⇒ ImportError: No module named 'a.b.c'; 'a.b' is not a package
import a.b
a ⇒ <module 'a' (namespace)>
a.__path__ ⇒ _NamespacePath(['/.../a'])
a.b ⇒ <module 'a.b' from '/home/cjs/tmp/a/b.py'>
a.b.__path__ ⇒ AttributeError: 'module' object has no attribute '__path__'
Python 确保在加载子模块之前加载所有父模块。在上面它发现 a/
是一个目录,因此创建了一个命名空间包 a
,而 a/b.py
是一个 Python 源文件,它加载并使用它来创建一个(非包)模块 a.b
。此时您不能有模块 a.b.c
,因为 a.b
不是包,因此不能有子模块。
您还可以在此处看到包模块 a
具有 __path__
属性(包必须具有此属性),但非包模块 a.b
没有。
[1, 2, 3]
不是列表,因为列表是内存中的对象一个 Python 解释器。吹毛求疵。
[1,2,3]
包括列表对象中的所有信息。文件不包含模块中的所有信息。例如,模块的名称取决于仅存在于解释器中的信息,不能仅从文件的内容或路径确定。尝试按照我上面的示例并仔细研究文档以提高您对模块的理解。
请务必记住,所有包都是模块,但并非所有模块都是包。或者换句话说,包只是一种特殊的模块。具体来说,任何包含 __path__ 属性的模块都被视为一个包。
名称中带有破折号的 Python 文件(如 my-file.py
)无法使用简单的 import
语句导入。在代码方面,import my-file
与 import my - file
相同,都会引发异常。此类文件更适合描述为 scripts,而可导入文件则是 modules。
这里的其他答案可能仍然有点模糊,所以我发布了一个希望更清晰的答案。重要的是要注意,问题的标题首先也有点误导,我认为更好的标题是:“与常规模块相比,包模块有什么特别之处?”。
TL;DR - 简答:
包也是模块,但是它们是它们的一种特殊类型。特殊的意义在于 1. 它们是“目录”,而 2. 它们可能包含特殊文件,例如 __init__.py
和 __main__.py
。
为了更好地理解 - 更长的答案:
关键是,包是一种特殊类型的模块,所以我们需要先了解模块的一般情况,然后包模块的特殊之处才会有意义。 (注意:我有时会在这个答案中将“包模块”称为“包”,反之亦然)
因此,让我们首先一般地讨论模块,因为它不那么模糊/更容易理解。基本上我们对模块做了两件事,我们要么将它们导入其他模块,要么直接由 Python 执行它们。
导入一个模块有一个明显的目标,即访问该模块内部的内容。
然而,执行一个模块通常追求以下两个目标之一:
该模块是一个主模块,执行它将启动我们的程序(或其子程序之一)。我们想单独尝试该模块的功能,即,不必先导入它。
让我们通过一些例子来更清楚地理解所有这些:
导入模块:
# bar.py
def talk():
print("bar")
# foo.py
import bar # <-- importing module "bar"
bar.talk() # <-- prints "bar"
执行模块
目标1,执行一个模块作为主模块:
假设上例中的 foo.py
模块是启动我们程序的主模块。我们可以通过在终端中输入以下命令来运行它:python3 foo.py # <-- executing a main module
,然后它将启动我们的程序。
目标 2,单独尝试模块的功能:
假设我们想尝试上面示例中bar.py
模块中的函数talk
,而不运行我们的整个程序,即不调用模块foo.py
。为此,我们必须稍微更改 bar.py
:
# bar.py
def talk():
print("bar")
if __name__ == '__main__':
talk()
现在在终端中运行此命令:python3 bar.py # <-- trying functionalities of a module in isolation
,然后它将打印 bar
。
现在我们知道了我们可以用模块做什么,让我们回到主要问题:
与常规模块相比,包模块有什么特别之处?
1. Python中的常规模块只是“文件”,而包模块是“目录”。
2. 常规模块可以“导入”,也可以“执行”(如上例所示),包模块也可以“导入”,可以“执行”,但是,你可能会抱怨:“但是我们可以'不要直接在目录中写代码!代码只写在文件中!”,这确实是一个很好的抱怨,因为它把我们引向了关于包模块的第二个特别的事情。包模块的代码写在其目录内的文件中,这些文件的名称也由 Python 保留。如果你想“导入”一个包模块,你必须把它的代码放在它目录下的 __init__.py 文件中,如果你想“执行”一个包模块,你必须把执行代码它在其目录中的 __main__.py 文件中。
这是上面解释的最后一个例子:
# hierarchy of files and folders:
.
├── bar_pack/
│ ├── __init__.py
│ ├── __main__.py
│ foo.py
# bar_pack/__init__.py
def talk():
print("bar")
# bar_pack/__main__.py
import __init__
__init__.talk()
# foo.py
import bar_pack # <-- importing package module "bar_pack"
bar_pack.talk() # <-- prints "bar"
# Run this command in the terminal:
python3 bar_pack # <-- executing the package module "bar_pack", prints "bar"
一个迟到的答案,另一个定义:
包由导入的顶级实体表示,该实体可以是自包含模块,也可以是 __init__.py 特殊模块,作为子目录结构中一组模块的顶级实体。
所以物理上一个包是一个分发单元,它提供一个或多个模块。
__init__.py
模块的目录,但如果您谈论分发单元(通常通过 PyPI),那么这完全是另一种类型的包(通常由 setup.py
的存在定义)。我发现术语 package
的这两种用法令人困惑,并且我与一些 Python 初学者交谈过,他们觉得它完全令人困惑。
模块:模块是带有 (.py
) 扩展名的简单 Python 文件,其中包含函数和全局变量的集合。它是一个可执行文件,Python 中 Package 的概念用于排列所有模块。
示例:将代码保存在名为 demo (module.py
) 的文件中。
def myModule1(name):
print("My Module name is: "+ name)
导入演示模块模块并在其中使用 myModule1 函数。
import demo_module
demo_module.myModule1("Math")
解决方案:
我的模块名称是:数学
包:包是包含模块集合的基本目录。此目录包含 Python 模块以及 (__init .py__
) 解释器用来将其识别为包的文件。包只不过是一个命名空间。在包中,有子包。
举个例子:
学生(套餐)
| __init__.py
(构造函数)
| details.py
(模块)
| marks.py
(模块)
| collegeDetails.py
(模块)
| demo_module.py
(模块)
包是组织成目录以形成包目录的一组模块。
from Student import details, collegeDetails, demo_module
我阅读了对这个问题的不同答案。该问题已完全涵盖。但在我看来,提出一个额外的观点可能不是一个坏主意。如果我们检查不同模块的 __package__ 的值,我们会得到以下结果。它们都是模块类型,但其中一些没有定义包。检查 __package__ 的“随机”和“数学”。
import cv2
import math
import random
import tkinter as tk
print('cv2:',type(cv2)) # <class 'module'>
print('cv2:',cv2) # <module 'cv2.cv2' from 'PATH'>
print('cv2:',cv2.__package__) # cv2
print('random:',type(random)) # <class 'module'>
print('random:',random) # <module 'random' from 'PATH'>
print('random:',random.__package__) # [EMPTY]
print('tk:',type(tk)) # <class 'module'>
print('tk:',tk) # <module 'tkinter' from 'PATH'>
print('tk:',tk.__package__) # tkinter
print('math:',type(math)) # <class 'module'>
print('math:',math) # <module 'math' (built-in)>
print('math:',math.__package__) # [EMPTY]
所以如果我们定义一个文件夹如下:
https://i.stack.imgur.com/tXU0t.png
这是我们可以看到 __package__ 输出的方式:
import myfolder
import myfolder.script1 as s1
import myfolder.script2 as s2
import myfolder.mySubfolder.script3 as s3
print(type(s1)) # <class 'module'>
print(type(s2)) # <class 'module'>
print(type(s3)) # <class 'module'>
print(s1.__package__) # myfolder
print(s2.__package__) # myfolder
print(s3.__package__) # myfolder.mySubfolder
print(myfolder) # <module 'myfolder' (namespace)>
print(myfolder.mySubfolder) # <module 'myfolder.mySubfolder' (namespace)>
print(myfolder.mySubfolder.script3) # <module 'myfolder.mySubfolder.script3' from 'PATH'>
print(myfolder.__package__) # myfolder
print(myfolder.mySubfolder.__package__) # myfolder.mySubfolder
print(myfolder.mySubfolder.script3.__package__) # myfolder.mySubfolder
我知道,为时已晚,但对某些人来说足够的简单答案是:
一个模块是一个文件,
一个包是一个文件夹。
module
类型。我正在编写调试器,并担心我的调试器说我的包是module
是不正确的。import
语句,因为 Python 标识符中不允许使用破折号。请改用importlib.import_module()
。