Python:构建一个项目,在不同级别的模块之间共享实用功能

Python: Structuring a project with utility functions shared across modules at different levels

我有一个 python 3.10 项目,它结合了抓取网站、数据分析和其他 API。一些实用程序模块可能会被抓取和数据分析模块使用。我从根本上误解了 Python 中导入的工作方式。 例如,在 sl_networking.py 中,我尝试从 result.py:

导入 Result class
from ...util.result import Result

产生错误:

PS C:\Development\TradeAssist> & c:/Development/TradeAssist/.venv/Scripts/python.exe c:/Development/TradeAssist/libs/scrapers/sl/sl_networking.py
Traceback (most recent call last):
  File "c:\Development\TradeAssist\libs\scrapers\sl\sl_networking.py", line 1, in <module>
    from ...util.result import Result
ImportError: attempted relative import with no known parent package

我目前使用的项目结构是:

TradeAssist
|__libs
|  |__broker_apis
|  |  |__ibapi
|  |__data_analysis
|  |  |__sl
|  |  |__ta
|  |__scrapers
|  |  |__sl
|  |  |  |  sl_auth.py
|  |  |  |__sl_networking.py
|  |  |__ta
|  |__util
|     |__result.py
|__tests
   |__test_sl.py
   |__test_ta.py

如果我有一个我希望在 data_analysisscraper 模块中使用的通用实用函数,我应该如何构建我的项目并处理导入?

相对导入仅在代码从最外层父根执行时有效。当前场景下只能执行libs目录以上的代码

python -m scrapers.sl.sl_networking

如果你在 libs 目录下 运行 应该可以正常工作。

项目结构化后,使用 -m 标志很容易 运行 顶级父目录中的各个脚本,因为不需要重构。如果必须从脚本父目录执行代码,则必须执行以下操作:

  1. 使用绝对导入而不是相对导入。
  2. 将目录添加到路径 python 搜索导入。这可以通过多种方式完成。将它添加到 PYTHONPATH env 变量或使用任何 sys.path.appendsys.path.insert hacks,很容易找到。

这不是 Python 问题,而是 VSCode 问题。默认情况下 VSCode 运行s Python 文件的方式有点笨。当您在 test_sl 上单击“运行 Python 文件”的小三角形时,VSCode 将 运行 这样的命令:

/usr/local/bin/python3 /path/to/TradeAssist/tests/test_ta.py

Python 默认初始化 sys.path 以包含包含文件 运行 的目录,除了它自己的库,以及你的 PYTHONPATH 变量中的任何内容.这意味着您的 sys.path 看起来像这样(... 是 Python 的库)

['/path/to/TradeAssist/tests', ...]

但是,这意味着 lib 中的代码无法访问; Python 只能正确找到 tests 内的文件。 IE。开箱即用,VSCode 只能 运行 Python 根源文件夹中的文件(例如 src,或直接在根工作区文件夹中)。

有几种解决方案。

第一个最简单:确定您的 TradeAssist 是源根目录,然后将要执行的任何文件直接放在那里。他们将能够导入其下的任何文件,并且其下的文件将能够正确使用相对和绝对导入。这有明显的缺点 — 您将 self-limiting 自己转到一个目录。

第二个是设置 VSCode 以通过定义 PYTHONPATH 告诉 Python 您的源根目录在哪里。这是fairly complex.

第三种是最简单的,也可能是正确的情况:使用 VSCode 的 testing functionality. Configure the testing framework (presumably using unittest and test_*.py test file pattern), then just run the tests。如果您认为 TradeAssist 是您的源根目录,它将正常工作(即 import libs.scrapers.sl.sl_networking 将在您的测试中正常工作,而 from ...util.result import Result 将在您的 sl_networking.py 中正常工作)。测试功能将运行以正确的方式为您进行测试。

但是,如果您想将 libs 视为您的源根目录(即您想要 import scrapers.sl.sl_networking),或者如果您希望能够 运行 任意文件,而不仅仅是测试,那么你必须回到方法#2:搞乱 PYTHONPATH.

tl;dr: 不要 运行 手动测试文件,让 VSCode 通过正确设置测试来为您完成。