下载了一个 python 项目,目录是这样子的

  • XXXProject

    • A
    • __init__.py
    • xxx.py
    • yyy.py
    • ...
    • scripts
    • zzz.sh

xxx.py 的内容是
from A.yyy import YY
if name == "__main__":
# .....

yyy.py 的内容是
class YYY():
# .....

zzz.sh 的内容是
python A/xxx.py

然后我就按照 README 所说,在项目根目录下执行 sh scripts/zzz.sh,结果报错了,错误是
ModuleNotFoundError: No module named 'A'

这是为什么呢?

按大家的说法....这就是这个项目的README本身写错了?

from yyy import YY

把 XXXProject 文件夹加到 python path 环境变量里

from future import absolute_import
from future import division
from future import print_function

import os.path as osp
import sys

def add_path(path):
if path not in sys.path:
sys.path.insert(0, path)

this_dir = osp.dirname(__file__)

lib_path = osp.join(this_dir, '..', 'lib')
add_path(lib_path)

需要加 path

精准命中
frostming.com/2019/03-13/where-do-your-packages-go/#%E8%84%9A%E6%9C%AC%E8%BF%90%E8%A1%8C%E6%96%B9%E5%BC%8F%E5%AF%B9%E6%90%9C%E7%B4%A2%E8%B7%AF%E5%BE%84%E7%9A%84%E5%BD%B1%E5%93%8D

要把 XXXProject 这个目录加到 PythonPATH 里面去

export PYTON_PATH=$PYTHON_PATH:/xxx/xxx/XXXProject

from yyy import YY +1 不需要A.yyy, 因为 xxx.py 和 yyy.py 在同一个文件夹下面

根目录自动添加的吧

谁给你自动添加? Python 怎么知道哪个目录是根目录

python 的 sys.path 第一位永远是""

你这个确实准

有的时候是不会在项目的根路径执行程序的

我觉得更可能你没有安装这个项目,因为 git clone 下来的不会自动安装
如果根目录下有 setup[.]py 的话,应先运行 pip install -e . (你可能会希望做一个 venv ,以免把 A 安装到系统里去)

我的观点,未验证:

  1. zzz.sh 的执行路径(在根目录)和执行方式( python A/xxx.py )没有问题
  2. 问题是此时 A/xxx.py 被看作为为一个脚本文件直接执行,却使用了 from A.yyy import YY 从它外部的一个包(其实是包括自己的包,但包不包括自己不重要)导入模块 yyy ,按理来说直接 from yyy import YY 就好了,不需要考虑 A ,直接把 yyy 作为同级模块导入即可
  3. 但是按照 2 的解决思路,A 的结构就显得很怪异,A 被强行看作一个包(有__init__.py ),但却又包含脚本。一般实践中,xxx.p 应该被拿出来,放在包外面,比如和 scripts/同级,然后在 zzz.sh 中 python xxx.py ,具体放哪看脚本干的事情,但放包里就是有问题

    发现排版不太好看清,

    pip install -e .

    最后的 dot 是必要的。

    确实挺怪的,不过一般实践应该是为 A.xxx:main 设置一个 entrypoint ,或者 python -m A.xxx

我认为 sys.path 都是扯犊子,我从来不那么干,除非在写加载动态化插件。Python 会把当前工作目录加入到 sys.path 里,这一点儿就够了。

直接改写 zzz.sh:
python ../A/xxx.py

sys.path.insert() 这种很脏的做法, 就不要再传播了.
Python 是通过 __init__.py 文件来区分 普通文件夹和 package 目录的.
你 XXXProject/ 目录下, 并没有__init__.py 文件, 你的脚本, 并不会认为这是 Python 项目.
很久不写 py 了. 建议你添加个 __init__.py 文件在 XXXProject/ 目录, 再试一下.
Python 的项目路径环境, 没什么复杂的.

Python 社区, 都凋零成这样了吗?
一个小脚本, 都开始教他添加环境变量了?
阿西吧.

你的回复, 还算正常. ls 的都是什么鬼.
你的回复, 配合我上面的建议的修改. 应该就正常了.

我用一楼的方法改好了。。。。但是谁能告诉我这是什么原因,python 版本问题吗?

这个项目我看也没有人提出相关的 issue ,肯定是能跑的

脏做法在顶流怎么解释
github.com/microsoft/scenepic/blob/eed95135ce5ea9705d1203102d4e53d1c8ea6046/docs/source/conf.py

基本上开源出来的算法模块现在都是这个套路吼

我就教了怎么了,你哪来的优越感?下次怼人之前麻烦先把你写的代码亮出来看看都是些什么东西

working_dir 决定一切

当你搞不清楚谁说的是对的时候,请查阅官方的文档
docs.python.org/3/tutorial/modules.html#the-module-search-path

简单来说先查内置 package 再查脚本的当前目录 再查 PYTHONPATH

你去看 flask 等项目的目录结构,
被 import 的路径里包含了__init__.py 文件。
而且官方文档也是建议这样做。

不要第一步就改环境变量,该设置的都在安装(软件和包)时设置好了

PEP 328: all import statements be absolute by default (searching sys.path only) with special syntax (leading dots) for accessing package-relative imports.
“from A.yyy import YY” 显然是个绝对导入,所以 python 肯定是在 sys.path 中的路径中查找 A 。。那程序要想执行只有两种方法:
1 、把 XXXProject 的路径加入 sys.path
2 、把 A 安装到现有的 sys.path 中去,比如 “lib/site-packages” 目录下

>>> import sys
>>> for x in sys.path: print(repr(x).replace(r'\\', '/'))
''
'C:/Python36/python36.zip'
'C:/Python36/DLLs'
'C:/Python36/lib'
'C:/Python36'
'C:/Python36/lib/site-packages'

当前目录确实在 sys.path 中,但当前目录指的是被 python.exe 直接执行的那个 xxx.py 所在的目录,所以 XXXProject/A 的路径在 sys.path 中,但 XXXProject 不在 sys.path 中。
所以题主的“from A.yyy import YY”执行出错,而一楼的“from yyy import YY”执行正常。

你没注意到这几行实际上被注释掉了吗…