为什么这些 Python 导入场景会以它们的方式工作(或不工作)?

Gre*_*son 6 python import

假设存储库有一个./example__init__.py文件调用的目录,以表明它是一个包。测试位于 中./tests,它没有文件__init__.py

.
|-- README.md
|-- example
|   |-- __init__.py
|   `-- func.py
|-- setup.py
`-- tests
    `-- test_hello.py
Run Code Online (Sandbox Code Playgroud)

./example/func.py很简单:

def hello():
    return "hello!"
Run Code Online (Sandbox Code Playgroud)

如果我在根目录中运行交互式解释器,我可以导入example

$ python
>>> import example
>>> example.hello()
'hello!'
Run Code Online (Sandbox Code Playgroud)

我还可以在导入的根目录中放置一个脚本example

$ cat > temp.py
import example
print(example.hello())
^D

$ python temp.py
hello!
Run Code Online (Sandbox Code Playgroud)

这就是事情变得奇怪的地方。如果我pytest在不带参数的根目录中运行,它将无法导入example

$ pytest
ImportError while importing test module '/Users/gregwilson/example/tests/test_hello.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../conda/envs/stjs/lib/python3.9/importlib/__init__.py:127: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests/test_hello.py:1: in <module>
    import example
E   ModuleNotFoundError: No module named 'example'
Run Code Online (Sandbox Code Playgroud)

python -m pytest但是,如果我在根目录中运行它会起作用:

$ python -m pytest
collected 1 item
tests/test_hello.py .
Run Code Online (Sandbox Code Playgroud)

如果我现在添加一个空文件tests/__init__.py,测试将以两种方式进行:

$ touch tests/_init__.py

$ python -m pytest
collected 1 item
tests/test_hello.py .

$ pytest
collected 1 item
tests/test_hello.py .
Run Code Online (Sandbox Code Playgroud)

我的问题是:

  1. 为什么会pytest失败但python -m pytest在添加之前就可以工作./tests/__init__.py
  2. 为什么添加一个__init__.py文件来tests制作pytest它自己的工作?

但是等等,事情变得更奇怪了。我可以创建一个目录./temp并添加一个./temp/temp.py导入example并尝试使用它的文件:

$ mkdir temp

$ cat > temp/temp.py
import example
print(example.hello())
^D
Run Code Online (Sandbox Code Playgroud)

直接运行是不行的:

$ python temp/temp.py
ModuleNotFoundError: No module named 'example'
Run Code Online (Sandbox Code Playgroud)

运行它python -m

$ python -m temp.temp
hello!
Run Code Online (Sandbox Code Playgroud)

但与这种tests情况不同的是,添加__init__.py文件并./temp 不会使其工作:

$ touch temp/__init__.py

$ python temp/temp.py
ModuleNotFoundError: No module named 'example'
Run Code Online (Sandbox Code Playgroud)

如果有人能解释这里发生了什么,我将不胜感激。