We're sunsetting PodQuest on 2025-07-28. Thank you for your support!
Export Podcast Subscriptions
cover of episode 222: Import within a Python package

222: Import within a Python package

2024/9/7
logo of podcast Test & Code

Test & Code

AI Deep Dive AI Insights AI Chapters Transcript
People
主持人
专注于电动车和能源领域的播客主持人和内容创作者。
Topics
在Python包的内部,模块间的导入方式有两种:`from . import module` 和 `import package`。前者用于包内模块间的导入,后者则允许外部程序像使用普通包一样使用当前包。本期节目讨论了在`cards`项目中,`cli.py` 使用 `import cards` 而不是 `from . import api` 的原因。这种方式使得`cli.py` 成为一个如何使用`cards` API 的外部示例,方便其他开发者学习和使用。 `cards` 项目包含四个文件:`db.py`(数据库层)、`api.py`(API层)、`cli.py`(命令行界面层)和 `__init__.py`(包初始化层)。它们之间存在清晰的分层结构,`cli.py` 使用 `api.py`,`api.py` 使用 `db.py`,`db.py` 不依赖其他模块。`__init__.py` 文件包含版本字符串,并从 `cli.py` 导入 `app` 对象,以及通过 `from .api import *` 导入 `api.py` 中定义的所有内容。`api.py` 使用 `from .db import DB` 导入数据库类 `DB`。 选择 `import cards` 的原因是为了让 `cli.py` 成为一个使用 `cards` API 的示例,方便其他项目参考。如果使用 `from . import api`,则 `cli.py` 就无法作为外部程序使用 `cards` 包的示例。通过在 REPL 中运行 `import cards` 并打印 `cards` 变量,可以查看 `cards` 变量的内容,它包含 `__init__.py` 中定义的内容。 设计良好的 API 应该易于测试和使用,`cli.py` 提供了另一个使用 API 的示例,有助于发现 API 中的问题。多个客户端使用 API 可以更好地测试 API 的易用性。将 `cli.py` 的导入语句改为 `from . import api` 后,需要修改其他代码并重新测试,最终发现这种修改并不比 `import cards` 更好,并且 `import cards` 更符合外部程序使用该包的方式。在开源项目中,提供一个使用 API 的示例非常有益。

Deep Dive

Key Insights

Why is the layering in the cards project important?

The layering in the cards project is important because it ensures a clear separation of concerns. The CLI layer handles the user interface, the API layer contains the business logic, and the DB layer manages database interactions. This strict separation prevents direct access between layers, ensuring that the CLI does not directly access the database and the API does not interact with the CLI, promoting modularity and maintainability.

What is the purpose of using `import cards` in CLI.py instead of `from . import API`?

Using `import cards` in CLI.py instead of `from . import API` allows the CLI to serve as an example of how to use the cards API from an external project. This approach mimics how a third-party application would import and use the cards package, making the CLI a practical reference for API usage. It also ensures that the CLI remains a true example of using the cards package as a library, rather than just importing internal modules directly.

What does the `cards` variable hold when imported in a REPL?

When `import cards` is executed in a REPL, the `cards` variable holds the module defined in `__init__.py`. This module includes the version string, the app object from CLI, and everything imported via `from .api import *`, which is controlled by the `__all__` list in `api.py`. Essentially, `cards` represents the top-level package interface.

What are the benefits of having multiple clients (like CLI and tests) for an API?

Having multiple clients for an API, such as CLI and tests, provides additional opportunities to evaluate the API's usability and design. If the API is difficult to use in tests or the CLI, it may indicate issues that need addressing before wider adoption. This approach aligns with test-driven development principles, ensuring the API is robust and user-friendly before being exposed to external users.

What happens if the version string is moved from `__init__.py` to `api.py`?

Moving the version string from `__init__.py` to `api.py` requires adding it to the `__all__` list in `api.py` to make it accessible. While this change can be made to fix test failures, it is not ideal because it disrupts the traditional placement of version strings, which are typically in `__init__.py` or `pyproject.toml`. Additionally, it makes the CLI less representative of how external users would interact with the package.

Chapters
This episode explores the nuances of importing modules within a Python package, specifically focusing on the choice between importing the entire package and importing only a specific module. The discussion stems from a question about the 'cards' application used in the book "Python Testing with PyTest", and it delves into the structure of the application's code.
  • The episode discusses importing within a Python package, comparing from . import module and import package.
  • The discussion is based on a question about the 'cards' application, with its structure explained.
  • The core question is why import package is preferred over from . import API in this context.

Shownotes Transcript

In this episode we're talking about importing part of a package into another part of the same package.

We'll look at: from . import module and from .module import something

and also:  import package to access the external API from with the package.

Why would we use import package if from . import api would work fine?

** Learn pytest**