【问题标题】:What's the difference between a Python module and a Python package?Python 模块和 Python 包有什么区别?
【发布时间】:2011-12-18 09:48:47
【问题描述】:

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

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

【问题讨论】:

标签: python module package


【解决方案1】:

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

一个模块就是一个文件,

一个包就是一个文件夹。

【讨论】:

    【解决方案2】:

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

    TL;DR - 简答:

    包也是模块,但是它们是它们的一种特殊类型。特殊的意义在于 1. 它们是“目录”,而 2. 它们可能包含特殊文件,例如 __init__.py__main__.py

    为了更好地理解 - 更长的答案:

    重点是,包是一种特殊类型的模块,所以我们需要先了解模块的一般性,然后包模块的特殊之处才有意义也。 (注意:我有时会在这个答案中将“包模块”称为“包”,反之亦然)

    所以让我们先从总体上谈一谈模块,因为它不那么模糊/更容易理解。 我们对模块主要做两件事,要么将它们导入其他模块,要么直接通过 Python 执行它们。

    导入一个模块有一个明确的目标,即访问该模块内部的内容。

    然而,执行一个模块通常会追求以下两个目标之一:

    1. 该模块是一个主模块,执行它会启动我们的程序(或其子程序之一)。
    2. 我们希望单独尝试该模块的功能,即无需先导入它。

    让我们通过一些例子来更清楚地理解所有这些:

    导入模块:

    # bar.py
    
    def talk():
        print("bar")
    
    # foo.py
    
    import bar # <-- importing module "bar"
    
    bar.talk() # <-- prints "bar"
    

    执行模块

    目标一,执行一个模块作为主模块:

    假设上面示例中的foo.py 模块是启动我们程序的主模块。我们可以在终端输入这个命令来运行它:python3 foo.py # &lt;-- 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 # &lt;-- 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"
    

    【讨论】:

      【解决方案3】:

      我阅读了对这个问题的不同答案。该问题已完全涵盖。但在我看来,提出一个额外的观点可能不是一个坏主意。如果我们检查不同模块的 __package__ 的值,我们会得到以下结果。它们都是模块类型,但其中一些没有定义包。检查 __package__ 中的“random”和“math”。

      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]
      

      所以如果我们定义一个文件夹如下:

      这是我们可以看到 __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
      

      【讨论】:

        【解决方案4】:

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

        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 模块。
        【解决方案5】:

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

        【讨论】:

        • 如果您还没有,请返回并完成此答案中的示例。
        • 同意这是一个非常有用的答案,因为它来自示例而不是一般性。
        【解决方案6】:

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

        一个包由一个导入的顶级实体表示,它可以 是一个独立的模块,或__init__.py 特殊模块作为 子目录结构中一组模块的顶级实体。

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

        【讨论】:

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

        来自Python glossary

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

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

        【讨论】:

          【解决方案8】:

          任何 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 类型。我正在编写调试器,并担心我的调试器说我的包是modules 是不正确的。
          • @jolvi 文件名包含破折号的 Python 文件仍然可以作为模块导入,只是不能使用通常的 import 语句,因为 Python 标识符中不允许使用破折号。请改用importlib.import_module()
          • @jolvi 我不是。你在我的评论中哪里读到的?我只是说,如果您碰巧拥有或偶然发现名称中带有破折号的 Python 文件,您仍然可以将其作为模块导入。我没有就命名 Python 文件的首选方式发表声明。我相信您可以在其他地方找到它:通常强烈建议避免使用破折号而使用下划线。
          • 作为 Python 新手,在导入父包时子包或模块默认不可用,这让我很困惑。有什么特别的原因吗?在导入父包时,如何使子包或模块可用(通过它们的完全限定名称)是否有一种通用模式?
          • @sschuberth 只需在父包的 init.py 中导入子包即可。
          猜你喜欢
          • 2010-10-17
          • 2011-04-10
          • 2016-04-23
          • 2011-03-01
          • 2019-12-15
          • 2010-10-02
          • 1970-01-01
          • 2012-07-11
          相关资源
          最近更新 更多