ChatGPT解决这个技术问题 Extra ChatGPT

Python 模块和 Python 包有什么区别?

Python 模块和 Python 包有什么区别?

另请参阅:What's the difference between "package" and "module"(适用于其他语言)

我可能错了,但对我来说:一个模块基本上是一个 python 文件。包是包含一堆模块(python 文件)的文件夹。
要被视为一个包,该文件夹必须包含一个 __init__.py 文件。
@lc2817:这是最常见的情况,但不需要从文件系统加载模块,例如 see from plumbum.cmd import ls implementation
@GiulioPiancastelli:在 Python 3.3+ 中,namespace packages do not use __init__.py
为了完整起见:从 Python 3.3 开始,该文件夹不需要包含 __init__.py 文件以被视为包。 @GiulioPiancastelli。请参阅PEP 420 -- Implicit Namespace Packages

N
Nathaniel Ford

任何 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 编写的,但我认为您不打算在您的问题中考虑这些。


感谢您明确提及 Python 创建的相应对象始终为 module 类型。我正在编写调试器,并担心我的调试器说我的包是 module 是不正确的。
@jolvi 文件名包含破折号的 Python 文件仍然可以作为模块导入,只是不能使用通常的 import 语句,因为 Python 标识符中不允许使用破折号。请改用 importlib.import_module()
@jolvi 我不是。你在我的评论中哪里读到的?我只是说,如果您碰巧拥有或偶然发现名称中带有破折号的 Python 文件,您仍然可以将其作为模块导入。我没有就命名 Python 文件的首选方式发表声明。我相信您可以在其他地方找到它:通常强烈建议避免使用破折号而使用下划线。
作为 Python 新手,在导入父包时默认情况下子包或模块不可用是让我绊倒的原因。有什么特别的原因吗?在导入父包时,是否有一种通用模式如何使子包或模块可用(通过它们的完全限定名称)?
@sschuberth 只需在父包的 init.py 中导入子包。
T
Tyler Marshall

模块是在一次导入下导入并使用的单个文件(或多个文件)。例如

import my_module

包是目录中提供包层次结构的模块的集合。

from my_package.timing.danger.internets import function_of_love

Documentation for modules

Introduction to packages


当您说:“一个模块是在一次导入下导入的单个文件(或多个文件)”时,您能解释一下模块是多个文件的情况吗?还是我误读了你的意思?
您不需要文件来创建模块,例如,您可以从 zip 文件中导入模块。包也一样。 Python 中的模块/包只有一个类。包只是一个具有 __path__ 属性的模块。
也是模块。它们只是包装不同;它们由目录加上 __init__.py 文件的组合形成。它们是可以包含其他模块的模块。
@Jacquot 当然,请参阅参考文档中的 The import system请务必记住,所有包都是模块
@Jacquot: 和 glossary on “package”: 一个 Python 模块,可以包含子模块或递归子包。从技术上讲,包是具有 __path__ 属性的 Python 模块。
c
cjs

首先,请记住,在其精确定义中,模块 是 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'>

模块 aa.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 没有。


如果您还没有,请返回并完成此答案中的示例。
同意这是一个非常有用的答案,因为它是由示例而非一般性驱动的。
“首先,请记住,在其精确定义中,模块是 Python 解释器内存中的对象” - 同样的道理,[1, 2, 3] 不是列表,因为列表是内存中的对象一个 Python 解释器。吹毛求疵。
@GiulioPiancastelli 一点也不挑剔:你错过了与你的例子的本质区别。 [1,2,3] 包括列表对象中的所有信息。文件包含模块中的所有信息。例如,模块的名称取决于仅存在于解释器中的信息,不能仅从文件的内容或路径确定。尝试按照我上面的示例并仔细研究文档以提高您对模块的理解。
M
Mihai Capotă

Python glossary

请务必记住,所有包都是模块,但并非所有模块都是包。或者换句话说,包只是一种特殊的模块。具体来说,任何包含 __path__ 属性的模块都被视为一个包。

名称中带有破折号的 Python 文件(如 my-file.py)无法使用简单的 import 语句导入。在代码方面,import my-fileimport my - file 相同,都会引发异常。此类文件更适合描述为 scripts,而可导入文件则是 modules


a
aderchox

这里的其他答案可能仍然有点模糊,所以我发布了一个希望更清晰的答案。重要的是要注意,问题的标题首先也有点误导,我认为更好的标题是:“与常规模块相比,包模块有什么特别之处?”。

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"

这是一个很棒的答案!
您在这里将文件与模块混淆。文件不是模块,它只是与其他信息一起可用于创建模块的信息,模块是内存中的对象。模块不必总是从文件中创建。 (有关更多详细信息和示例,请参阅我的答案。)
B
Bram Vanroy

一个迟到的答案,另一个定义:

包由导入的顶级实体表示,该实体可以是自包含模块,也可以是 __init__.py 特殊模块,作为子目录结构中一组模块的顶级实体。

所以物理上一个包是一个分发单元,它提供一个或多个模块。


我觉得 Python 中的 package 有两个定义,它们是不同的。您的答案似乎将它们结合在一起。严格来说,python 包是一个包含 __init__.py 模块的目录,但如果您谈论分发单元(通常通过 PyPI),那么这完全是另一种类型的包(通常由 setup.py 的存在定义)。我发现术语 package 的这两种用法令人困惑,并且我与一些 Python 初学者交谈过,他们觉得它完全令人困惑。
@davidA,这不仅仅是你的感受。它已被编纂:packaging.python.org/glossary/#term-distribution-package(也感谢您的澄清!)
M
Md Mahadi Hasan Sany

模块:模块是带有 (.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

S
Shamshirsaz.Navid

我阅读了对这个问题的不同答案。该问题已完全涵盖。但在我看来,提出一个额外的观点可能不是一个坏主意。如果我们检查不同模块的 __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

A
Andrew Anderson

我知道,为时已晚,但对某些人来说足够的简单答案是:

一个模块是一个文件,

一个包是一个文件夹。