python - 如何构建面向对象的 Python 3 项目及其导入?

标签 python python-3.x

我有一个面向对象的 Python 3.7 项目,其结构如下:

├── plugins
│   ├── book_management
│   │   ├── book_inserter.py
│   │   ├── book_remover.py
│   │   ├── __init__.py
│   │   ├── book.py
│   │   ├── book_sampler.py
│   │   ├── operators
│   │   │   ├── __init__.py
│   │   │   ├── register_book.py
│   │   │   ├── unregister_book.py
│   │   │   └── mark_book_as_missing.py
│   ├── __init__.py
│   ├── reader_management
│   │   ├── __init__.py
│   │   ├── reader.py
│   │   ├── reader_creator.py
│   │   ├── reader_emailer.py
│   │   ├── reader_remover.py
│   │   ├── operators
│   │   │   ├── __init__.py
│   │   │   ├── create_reader.py
│   │   │   ├── remove_reader.py
│   │   │   └── email_reader.py
├── tests
│   ├── __init__.py
│   ├── book_management_tests
│   │   ├── __init__.py
│   │   ├── test_book.py
│   │   ├── test_book_inserter.py
│   │   ├── test_book_remover.py
│   │   ├── test_book_sampler.py
│   │   ├── test_mark_book_as_missing_operator.py
│   │   ├── test_register_book_operator.py
│   │   ├── test_unregister_book_operator.py
│   ├── reader_management_tests
│   │   ├── __init__.py
│   │   ├── test_reader.py
│   │   ├── test_reader_creator.py

在像 test_mark_book_as_missing_operator 这样的测试中,我最终得到了这样的导入:

from plugins.book_management.book_inserter import BookInserter
from plugins.book_management.operators.mark_book_as_missing import (
    MarkBookAsMissingOperator
)
from plugins.reader_management.reader_creator import ReaderCreator
from plugins.reader_management.operators.create_reader import (
    CreateReaderOperator
)

这些非常冗长的部分导入感觉真的很糟糕。所以我猜我一定做错了。理想情况下,将 plugins.reader_managementplugins.reader_management.operators 导入可能更短的内容似乎更具可读性。

book_inserter.py 定义了一个类 BookInserter。理想情况下,我想保留这个 1-class/1-file 结构。显然,这会导致文件数量的膨胀,但也允许更短的更集中的文件。但如果这是非常非 Pythonic 的,我愿意听听为什么以及如何调整代码结构。

最后,我一直在使用这种多层架构 (plugins/*_management/operators/*.py),但这会导致很长的导入行,而且我经常遇到合法的 lint 问题结果。

我一直在考虑从顶级模块(比如 book_management,在 book_management/__init__.py 中)导入子模块,但我不确定这是否是好的做法,而且这似乎违反了原则在您的文件中没有未使用的导入。 (因此我是否会面临循环导入的风险?)

简而言之,我的主要问题是:构建这样一个项目并设置导入的(?)Pythonic 方式是什么(最好有一些理由说明为什么这会是一种 Pythonic 方式)。

最佳答案

It is perfectly fine使用 __init__.py 压缩您的命名空间。使用 __all__ 明确定义导入的名称是为了导出。

# plugins/book_management/__init__.py
from .book_inserter import BookInserter
from .operators.mark_book_as_missing import MarkBookAsMissingOperator
# more imports

__all__ = [
    'BookInserter',
    'MarkBookAsMissingOperator',
    # more exports
]

这减少了导入使用的长度和数量:

# test_mark_book_as_missing_operator
from plugins.book_management import BookInserter, MarkBookAsMissingOperator
from plugins.reader_management import ReaderCreator, CreateReaderOperator

对于每个文件 1 个定义是否是一件的事情似乎没有达成共识。不过,对于标准库和许多第三方模块,习惯上将所有直接相关的类和函数放在一起。

关于python - 如何构建面向对象的 Python 3 项目及其导入?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59790236/

相关文章:

python - 这个异步 aiohttp 代码有什么问题?

python - 过滤小数时 Django Postgresql DatabaseError

Python:将类变量字符串转换为类类字符串

python - 在python([]或list())中初始化空列表的最佳实践是什么?

python - 按百分比获取随机 bool 值

python - matplotlib 取消轴对象上的图例

python - 将列表中的整数与其索引相乘

python-3.x - Flask,无法通过 request.files 获取文件部分

python - 圣人语法 A.<x>

python - 使用 aiohttp.ClientSession() 发出请求时如何为 aiohttp.client 设置日志记录?