【问题标题】:What is the best project structure for a Python application? [closed]Python 应用程序的最佳项目结构是什么? [关闭]
【发布时间】:2010-09-16 15:05:29
【问题描述】:

假设您想用 Python 开发一个重要的最终用户桌面(不是 Web)应用程序。构建项目文件夹层次结构的最佳方式是什么?

理想的功能是易于维护、IDE 友好、适合源代码控制分支/合并以及易于生成安装包。

特别是:

  1. 你把源放在哪里?
  2. 您将应用程序启动脚本放在哪里?
  3. 您将 IDE 项目放在哪里?
  4. 您将单元/验收测试放在哪里?
  5. 您将非 Python 数据(例如配置文件)放在哪里?
  6. 对于 pyd/so 二进制扩展模块,您将非 Python 源代码(例如 C++)放在哪里?

【问题讨论】:

    标签: python directory-structure organization project-structure


    【解决方案1】:

    没什么大不了的。任何让你开心的事情都会奏效。没有太多愚蠢的规则,因为 Python 项目可以很简单。

    • /scripts/bin 用于那种命令行界面的东西
    • /tests 供您测试
    • /lib 用于您的 C 语言库
    • /doc 获取大部分文档
    • /apidoc 用于 Epydoc 生成的 API 文档。

    顶级目录可以包含README、Config等。

    艰难的选择是是否使用/src 树。 Python 不像 Java 或 C 那样区分 /src/lib/bin

    由于某些人认为顶级 /src 目录毫无意义,因此您的顶级目录可以是您应用程序的顶级架构。

    • /foo
    • /bar
    • /baz

    我建议将所有这些都放在“我的产品名称”目录下。因此,如果您正在编写一个名为 quux 的应用程序,则包含所有这些内容的目录将命名为 /quux

    那么,另一个项目的PYTHONPATH 可以包含/path/to/quux/foo 以重用QUUX.foo 模块。

    在我的例子中,由于我使用 Komodo Edit,我的 IDE cuft 是单个 .KPF 文件。我实际上把它放在顶级 /quux 目录中,并省略了将其添加到 SVN。

    【讨论】:

    • 你会推荐任何开源 python 项目来模拟它们的目录结构吗?
    • 看看 Django 的一个很好的例子。
    • 我不倾向于认为 Django 是一个很好的例子——在我的书中玩 sys.path 是一个即时 DQ。
    • re "tricks": Django 将根项目文件夹的父级添加到 sys.path 中,以便模块可以作为“from project.app.module import klass”或“from app”导入.module 导入类”。
    • 哦,我喜欢这个技巧,现在正在使用它。我想将共享模块放在另一个目录中,我不想在系统范围内安装模块,也不想要求人们手动修改 PYTHONPATH。除非人们提出更好的建议,否则我认为这实际上是最干净的方法。
    【解决方案2】:

    根据 Jean-Paul Calderone 的Filesystem structure of a Python project

    Project/
    |-- bin/
    |   |-- project
    |
    |-- project/
    |   |-- test/
    |   |   |-- __init__.py
    |   |   |-- test_main.py
    |   |   
    |   |-- __init__.py
    |   |-- main.py
    |
    |-- setup.py
    |-- README
    

    【讨论】:

    • bin文件夹中的可执行文件如何引用项目模块? (我不认为 python 语法允许 ../ 在包含语句中)
    • @ThorSummoner 仅在保留在单个包中时才有效。要在此处进行相对导入,您需要在 bin 文件夹和 Project 顶级文件夹中都有一个 __init__.py 文件。
    • @ThorSummoner 简单。你安装包! (pip install -e /path/to/Project)
    • 如果有人能用 hello.py 和 hello-test.py 压缩这个布局的示例并提供给我们新手使用,那就太棒了。
    • @Bloke 核心是-e 标志,它将包安装为可编辑包,即安装为指向实际项目文件夹的链接。然后可执行文件只需import project 即可访问该模块。
    【解决方案3】:

    blog post by Jean-Paul Calderone 通常在 Freenode 上的#python 中作为答案给出。

    Python 项目的文件系统结构

    做:

    • 将目录命名为与您的项目相关的名称。例如,如果您的项目名为“Twisted”,请将其源文件的顶级目录命名为 Twisted。发布时,应包含版本号后缀:Twisted-2.5
    • 创建一个目录Twisted/bin 并把你的可执行文件放在那里,如果你有的话。不要给他们.py 扩展名,即使他们是 Python 源文件。除了导入和调用项目中其他地方定义的主函数外,不要在其中放置任何代码。 (轻微的皱纹:因为在 Windows 上,解释器是由文件扩展名选择的,所以你的 Windows 用户确实想要 .py 扩展名。所以,当你为 Windows 打包时,你可能想要添加它。不幸的是,没有简单的 distutils 技巧我知道要自动化这个过程。考虑到在 POSIX 上,.py 扩展名只是一个缺点,而在 Windows 上,缺少的是一个实际的错误,如果您的用户群包括 Windows 用户,您可能希望选择只拥有 .py到处都是扩展。)
    • 如果您的项目可表示为单个 Python 源文件,则将其放入该目录并命名为与您的项目相关的名称。例如,Twisted/twisted.py。如果您需要多个源文件,请改为创建一个包(Twisted/twisted/,使用空的Twisted/twisted/__init__.py)并将源文件放入其中。例如,Twisted/twisted/internet.py
    • 将单元测试放在包的子包中(注意 - 这意味着上面的单个 Python 源文件选项是一个技巧 - 您总是需要至少一个其他文件用于您的单元测试)。例如,Twisted/twisted/test/。当然,用Twisted/twisted/test/__init__.py 把它做成一个包。将测试放在 Twisted/twisted/test/test_internet.py 之类的文件中。
    • 添加 Twisted/READMETwisted/setup.py 分别解释和安装您的软件,如果您感觉不错的话。

    不要:

    • 将您的源代码放在名为srclib 的目录中。这使得不安装就很难运行。
    • 将测试放在 Python 包之外。这使得很难针对已安装的版本运行测试。
    • 创建一个__init__.py的包,然后将所有代码放入__init__.py。只需制作一个模块而不是一个包,它更简单。
    • 尝试想出一些神奇的技巧,让 Python 能够导入您的模块或包,而无需用户将包含它的目录添加到他们的导入路径(通过 PYTHONPATH 或其他一些机制)。您将不会正确处理所有情况,当您的软件无法在他们的环境中运行时,用户会生您的气。

    【讨论】:

    • 这正是我所需要的。 “不要试图想出神奇的技巧来让 Python 能够导入你的模块或包,而无需用户将包含它的目录添加到他们的导入路径中。”很高兴知道!
    • 对“将源代码放在名为 src 或 lib 的目录中。这使得不安装很难运行。”感到困惑。会安装什么?是导致问题的目录名称,还是它根本就是一个子目录这一事实?
    • “有些人会断言您应该在模块本身内分发测试——我不同意。这通常会增加用户的复杂性;许多测试套件通常需要额外的依赖项和运行时上下文。” python-guide-pt-br.readthedocs.io/en/latest/writing/structure/…
    • "这使得不安装就很难运行。" -- 这就是
    • 我觉得具有讽刺意味的是,该示例使用Twisted 作为项目名称,因为官方Twisted library 现在使用src 布局,这与第一个“不要”建议相矛盾:“将您的源代码放在一个名为 src 或 lib 的目录中。这使得不安装就很难运行。”这就是重点(见Ionel Cristian Mărieș's article)。
    【解决方案4】:

    查看Open Sourcing a Python Project the Right Way

    让我摘录那篇优秀文章的项目布局部分:

    在设置项目时,布局(或目录结构)对于确保正确非常重要。合理的布局意味着潜在的贡献者不必永远花时间寻找一段代码;文件位置很直观。由于我们正在处理现有项目,这意味着您可能需要移动一些东西。

    让我们从顶部开始。大多数项目都有许多顶级文件(如 setup.py、README.md、requirements.txt 等)。那么每个项目都应该有三个目录:

    • 包含项目文档的 docs 目录
    • 以项目名称命名的目录,用于存储实际的 Python 包
    • 两个地方之一的测试目录
      • 包含测试代码和资源的包目录下
      • 作为独立的顶级目录 为了更好地了解文件的组织方式,下面是我的一个项目 Sandman 的布局简化快照:
    $ pwd
    ~/code/sandman
    $ tree
    .
    |- LICENSE
    |- README.md
    |- TODO.md
    |- docs
    |   |-- conf.py
    |   |-- generated
    |   |-- index.rst
    |   |-- installation.rst
    |   |-- modules.rst
    |   |-- quickstart.rst
    |   |-- sandman.rst
    |- requirements.txt
    |- sandman
    |   |-- __init__.py
    |   |-- exception.py
    |   |-- model.py
    |   |-- sandman.py
    |   |-- test
    |       |-- models.py
    |       |-- test_sandman.py
    |- setup.py
    

    如您所见,有一些顶级文件,一个 docs 目录(生成的是一个空目录,sphinx 将在其中放置生成的文档),一个 sandman 目录,以及 sandman 下的一个 test 目录。

    【讨论】:

    • 我这样做了,但更重要的是:我有一个顶层 Makefile,它带有一个 'env' 目标,可以自动执行 'virtualenv env ; ./env/bin/pip install -r requirements.txt ; ./env/bin/python setup.py develop',通常也是一个依赖于 env 并安装测试依赖项然后运行 ​​py.test 的“测试”目标。
    • @pjz 你能扩展你的想法吗?您是在说将Makefilesetup.py 放在同一级别吗?因此,如果我对您的理解正确,make env 会自动创建一个新的venv 并将软件包安装到其中...?
    • @St.Antario 完全正确。如前所述,我通常还有一个“测试”目标来运行测试,有时还有一个“发布”目标,它查看当前标签并构建一个轮子并将其发送给 pypi。
    • 在这个结构中,/code/sandman/sandman/ 中的任何文件如何导入/code/sandman/docs/ 中的内容?说,我想从sandman.py 导入config.py。我该怎么做?
    【解决方案5】:

    “Python Packaging Authority”有一个示例项目:

    https://github.com/pypa/sampleproject

    这是一个示例项目,作为 Python 打包用户指南的打包和分发项目教程的辅助工具。

    【讨论】:

    【解决方案6】:

    尝试使用python_boilerplate 模板启动项目。它在很大程度上遵循最佳实践(例如those here),但更适合您发现自己愿意在某个时候将您的项目拆分为多个鸡蛋(相信我,除了最简单的项目之外,您会的。一种常见的情况是您必须使用其他人的库的本地修​​改版本。

    • 你把源放在哪里?

      • 对于相当大的项目,将源分成几个鸡蛋是有意义的。每个鸡蛋都将作为PROJECT_ROOT/src/<egg_name> 下的单独设置工具布局。
    • 您将应用程序启动脚本放在哪里?

      • 理想的选择是在其中一个鸡蛋中将应用程序启动脚本注册为entry_point
    • 你把 IDE 项目放在哪里?

      • 取决于 IDE。他们中的许多人将他们的东西保存在项目根目录中的PROJECT_ROOT/.<something> 中,这很好。
    • 您将单元/验收测试放在哪里?

      • 每个鸡蛋都有一组单独的测试,保存在其PROJECT_ROOT/src/<egg_name>/tests 目录中。我个人更喜欢使用py.test 来运行它们。
    • 您将非 Python 数据(例如配置文件)放在哪里?

      • 视情况而定。可以有不同类型的非 Python 数据。
        • “资源”,即必须打包在鸡蛋中的数据。该数据进入相应的 egg 目录,位于包命名空间中的某处。它可以通过setuptools 中的pkg_resources 包使用,或者从Python 3.7 开始通过标准库中的importlib.resources 模块使用。
        • “Config-files”,即非 Python 文件,它们被视为项目源文件的外部文件,但在应用程序开始运行时必须使用一些值进行初始化。在开发过程中,我更喜欢将这些文件保存在PROJECT_ROOT/config 中。对于部署,可以有多种选择。在 Windows 上可以使用%APP_DATA%/<app-name>/config,在 Linux 上可以使用/etc/<app-name>/opt/<app-name>/config
        • 生成的文件,即在执行期间可能由应用程序创建或修改的文件。我希望在开发期间将它们保留在 PROJECT_ROOT/var 中,在 Linux 部署期间将它们保留在 /var 中。
    • 对于 pyd/so 二进制扩展模块,您将 C++ 等非 Python 源代码放在哪里?
      • 进入PROJECT_ROOT/src/<egg_name>/native

    文档通常会进入PROJECT_ROOT/docPROJECT_ROOT/src/<egg_name>/doc(这取决于您是否将某些鸡蛋视为一个单独的大型项目)。一些额外的配置将在 PROJECT_ROOT/buildout.cfgPROJECT_ROOT/setup.cfg 等文件中。

    【讨论】:

    • 感谢您的精彩回答!你为我澄清了很多事情!我只有一个问题:鸡蛋可以嵌套吗?
    • 不,你不能在将 .egg 文件存储在其他 .egg 文件中的意义上“嵌套”鸡蛋,并希望这会有很大用处 [除非你想做一些非常奇怪的事情] .但是,您可以做的是创建“虚拟”鸡蛋 - 不提供任何有用代码的空包,但在其依赖项列表中列出其他包。这样,当用户尝试安装这样的包时,他会递归地安装许多依赖的鸡蛋。
    • @KT 你能详细说明一下你如何处理生成的数据吗?特别是,您(在代码中)如何区分开发和部署?我想你有一些 base_data_location 变量,但你如何正确设置它?
    • 我想你说的是“运行时数据”——人们经常会放在 /var/packagename 或 ~/.packagename/var 或诸如此类的东西下。大多数情况下,这些选择足以作为您的用户不想更改的默认设置。如果您想调整此行为,则选项相当丰富,我认为没有一个适合所有的最佳实践。典型选择:a) ~/.packagename/configfile, b) export MY_PACKAGE_CONFIG=/path/to/configfile c) 命令行选项或函数参数 d) 这些组合。
    • 请注意,在某个地方有一个单例 Config 类是很常见的,它为您处理您最喜欢的配置加载逻辑,甚至可能让用户在运行时修改设置。不过,总的来说,我认为这是一个值得单独提出一个问题的问题(之前可能在这里的某个地方被问过)。
    【解决方案7】:

    根据我的经验,这只是迭代的问题。将您的数据和代码放在您认为的任何地方。有可能,无论如何你都会错的。但是,一旦您更好地了解事情将如何发展,您就可以更好地进行这些猜测。

    就扩展源而言,我们在主干下有一个代码目录,其中包含一个用于 python 的目录和一个用于各种其他语言的目录。就个人而言,我更倾向于下次尝试将任何扩展代码放入自己的存储库中。

    话虽如此,我还是回到我最初的观点:不要大惊小怪。把它放在似乎对你有用的地方。如果您发现某些东西不起作用,则可以(并且应该)对其进行更改。

    【讨论】:

    • 是的。我试图成为“Pythonic”:显式优于隐式。目录层次结构的读取/检查比写入更多。等等。
    【解决方案8】:

    使用setuptools 中的package_data 支持,最好将非python 数据捆绑在您的Python 模块中。我强烈推荐的一件事是使用命名空间包来创建多个项目可以使用的共享命名空间——很像将包放入com.yourcompany.yourproject 的Java 约定(并且能够拥有一个共享的com.yourcompany.utils 命名空间)。

    重新分支和合并,如果您使用足够好的源代码控制系统,它甚至可以通过重命名来处理合并; Bazaar 特别擅长这个。

    与此处的其他一些答案相反,我对拥有 src 顶级目录(旁边有 doctest 目录)表示 +1。文档目录树的特定约定将根据您使用的内容而有所不同;例如,Sphinx 有其快速入门工具支持的自己的约定。

    请利用 setuptools 和 pkg_resources;这使得其他项目更容易依赖您的代码的特定版本(如果您使用的是package_data,则可以使用不同的非代码文件同时安装多个版本)。

    【讨论】:

    • 狮身人面像的链接不可用
    • @FranzKurt,感谢您的提醒;更新了链接。
    猜你喜欢
    • 2010-10-09
    • 1970-01-01
    • 2015-12-19
    • 2017-02-03
    • 2015-08-07
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 2014-05-23
    相关资源
    最近更新 更多