Python 包导入的困惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
zxCoder
V2EX    Python

Python 包导入的困惑

  •  
  •   zxCoder 2021-11-05 08:52:33 +08:00 5083 次点击
    这是一个创建于 1437 天前的主题,其中的信息可能已经有所发展或是发生改变。

    下载了一个 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' 

    这是为什么呢?

    第 1 条附言    2021-11-06 09:06:57 +08:00

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

    39 条回复    2021-11-08 14:00:36 +08:00
    mangoDB
        1
    mangoDB  
       2021-11-05 09:00:00 +08:00
    from yyy import YY
    Trim21
        2
    Trim21  
       2021-11-05 09:02:45 +08:00 via Android
    把 XXXProject 文件夹加到 python path 环境变量里
    Latin
        3
    Latin  
       2021-11-05 09:18:43 +08:00
    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)
    ch2
        4
    ch2  
       2021-11-05 09:43:11 +08:00
    需要加 path
    misaka19000
        6
    misaka19000  
       2021-11-05 09:51:31 +08:00
    要把 XXXProject 这个目录加到 PythonPATH 里面去

    export PYTON_PATH=$PYTHON_PATH:/xxx/xxx/XXXProject
    sudoy
        7
    sudoy  
       2021-11-05 09:54:31 +08:00
    @mangoDB `from yyy import YY` +1 不需要`A.yyy`, 因为 xxx.pyyyy.py 在同一个文件夹下面
    Vegetable
        8
    Vegetable  
       2021-11-05 09:57:09 +08:00
    @misaka19000 根目录自动添加的吧
    misaka19000
        9
    misaka19000  
       2021-11-05 10:02:35 +08:00
    @Vegetable 谁给你自动添加? Python 怎么知道哪个目录是根目录
    Vegetable
        10
    Vegetable  
       2021-11-05 10:18:09 +08:00
    @misaka19000 python 的 sys.path 第一位永远是""
    Vegetable
        11
    Vegetable  
       2021-11-05 10:20:40 +08:00
    @frostming 你这个确实准
    misaka19000
        12
    misaka19000  
       2021-11-05 10:23:14 +08:00
    @Vegetable 有的时候是不会在项目的根路径执行程序的
    2i2Re2PLMaDnghL
        13
    2i2Re2PLMaDnghL  
       2021-11-05 11:08:32 +08:00
    我觉得更可能你没有安装这个项目,因为 git clone 下来的不会自动安装
    如果根目录下有 setup[.]py 的话,应先运行 pip install -e . (你可能会希望做一个 venv ,以免把 A 安装到系统里去)
    jaredyam
        14
    jaredyam  
       2021-11-05 11:10:39 +08:00
    我的观点,未验证:
    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 ,具体放哪看脚本干的事情,但放包里就是有问题
    2i2Re2PLMaDnghL
        15
    2i2Re2PLMaDnghL  
       2021-11-05 11:11:25 +08:00
    @2i2Re2PLMaDnghL 发现排版不太好看清,
    ```
    pip install -e .
    ```
    最后的 dot 是必要的。
    2i2Re2PLMaDnghL
        16
    2i2Re2PLMaDnghL  
       2021-11-05 11:15:13 +08:00
    @jaredyam 确实挺怪的,不过一般实践应该是为 A.xxx:main 设置一个 entrypoint ,或者 python -m A.xxx
    xingheng
        17
    xingheng  
       2021-11-05 14:33:57 +08:00
    我认为 sys.path 都是扯犊子,我从来不那么干,除非在写加载动态化插件。Python 会把当前工作目录加入到 sys.path 里,这一点儿就够了。

    直接改写 zzz.sh:
    python ../A/xxx.py
    akaHenry
        18
    akaHenry  
       2021-11-05 16:49:34 +08:00
    @Latin sys.path.insert() 这种很脏的做法, 就不要再传播了.


    Python 是通过 __init__.py 文件来区分 普通文件夹和 package 目录的.


    你 XXXProject/ 目录下, 并没有__init__.py 文件, 你的脚本, 并不会认为这是 Python 项目.


    很久不写 py 了. 建议你添加个 __init__.py 文件在 XXXProject/ 目录, 再试一下.


    Python 的项目路径环境, 没什么复杂的.
    akaHenry
        19
    akaHenry  
       2021-11-05 16:51:23 +08:00
    @misaka19000 Python 社区, 都凋零成这样了吗?

    一个小脚本, 都开始教他添加环境变量了?

    阿西吧.
    akaHenry
        20
    akaHenry  
       2021-11-05 16:54:50 +08:00
    @xingheng 你的回复, 还算正常. ls 的都是什么鬼.

    你的回复, 配合我上面的建议的修改. 应该就正常了.
    zxCoder
        21
    zxCoder  
    OP
       2021-11-05 17:02:06 +08:00
    @orzglory 我用一楼的方法改好了。。。。但是谁能告诉我这是什么原因,python 版本问题吗?

    这个项目我看也没有人提出相关的 issue ,肯定是能跑的
    Latin
        22
    Latin  
       2021-11-05 17:07:52 +08:00
    Latin
        23
    Latin  
       2021-11-05 17:08:32 +08:00
    基本上开源出来的算法模块现在都是这个套路吼
    misaka19000
        24
    misaka19000  
       2021-11-05 17:26:54 +08:00   2
    @orzglory 我就教了怎么了,你哪来的优越感?下次怼人之前麻烦先把你写的代码亮出来看看都是些什么东西
    oOoOoOoOoOo
        25
    oOoOoOoOoOo  
       2021-11-05 20:40:41 +08:00 via Android
    working_dir 决定一切
    manfredexz
        26
    manfredexz  
       2021-11-05 21:05:24 +08:00
    当你搞不清楚谁说的是对的时候,请查阅官方的文档
    https://docs.python.org/3/tutorial/modules.html#the-module-search-path

    简单来说先查内置 package 再查脚本的当前目录 再查 PYTHONPATH
    ila
        27
    ila  
       2021-11-05 21:28:13 +08:00 via Android
    你去看 flask 等项目的目录结构,
    被 import 的路径里包含了__init__.py 文件。
    而且官方文档也是建议这样做。
    ila
        28
    ila  
       2021-11-05 21:29:25 +08:00 via Android
    不要第一步就改环境变量,该设置的都在安装(软件和包)时设置好了
    XIVN1987
        29
    XIVN1987  
       2021-11-05 21:48:25 +08:00
    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” 目录下
    XIVN1987
        30
    XIVN1987  
       2021-11-05 2:04:50 +08:00
    ```
    >>> 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”执行正常。
    Trim21
        31
    Trim21  
       2021-11-05 22:32:26 +08:00 via Android
    @Latin 你没注意到这几行实际上被注释掉了吗…
    akaHenry
        32
    akaHenry  
       2021-11-08 12:38:20 +08:00
    @zxCoder 我的解释是原因, 你按照我的改法, 添加一个 init.py 文件(补上面的下划线, v 站说这是链接). 应该不用改代码就可以跑. 原因, 我解释了哇.

    from A.yyy , 这里 A 是 package 的前提是, A 同级目录, 要有 init.py 文件. 才是 Python package 目录, 否则只是普通文件夹.
    akaHenry
        33
    akaHenry  
       2021-11-08 12:40:56 +08:00
    @Trim21 所以这些人, 真的可爱. 贴个注释给我看.

    27 楼, 跟我指出的问题. 是一致的.

    这些不懂装懂的, 就不要出来误导别人.

    这下面, 一排连 Python package 模块目录和 普通目录都没搞清楚的, 这基本功, 够可以.
    akaHenry
        34
    akaHenry  
       2021-11-08 12:46:55 +08:00
    @misaka19000 不懂装懂, 指出来. 就跳脚.

    py 社区, 就剩下这种水平的出来胡说八道. 可悲.
    Trim21
        35
    Trim21  
       2021-11-08 12:48:58 +08:00
    @orzglory 没有__init__.py 文件的还有可能是 python namespace package 啊...
    akaHenry
        36
    akaHenry  
       2021-11-08 12:51:29 +08:00
    @Trim21 lz 的这个脚本. 显然与此无关呀.
    akaHenry
        37
    akaHenry  
       2021-11-08 12:55:49 +08:00
    namespace package 是给 setup.py 这种安装脚本用的. 不是给日常场景滥用的.

    这个特性本身, 我认为只是为了 hack 一些问题而打的补丁.
    onlylovehuan
        38
    onlylovehuan  
       2021-11-08 13:27:35 +08:00
    跑的时候命令行先跑到 XXXProject 路径下面,再执行
    O5oz6z3
        39
    O5oz6z3  
       2021-11-08 14:00:36 +08:00
    #5 和#26 楼的文档说完了。项目可能没问题如#13 #29 所说需要全局安装。
    #26 docs.python.org/zh-cn/3/tutorial/modules.html#the-module-search-path
    #12 docs.python.org/zh-cn/3/using/cmdline.html#cmdarg-script
    #6 docs.python.org/zh-cn/3/using/cmdline.html#envvar-PYTHONPATH
    #16 docs.python.org/zh-cn/3/using/cmdline.html#cmdoption-m
    __init__.py 只是规范问题,加不加效果上我猜区别不大……
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2732 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 08:31 PVG 16:31 LAX 01:31 JFK 04:31
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86