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
ztm0929
V2EX  ›  Python

Python 导入自定义包的正确做法是什么?

  •  
  •   ztm0929 ·
    ztm0929 · 26 天前 · 2639 次点击

    我是编程新手,正在练习爬虫项目,Python 到底如何导入包?

    xxx_project
    ├── README
    └── app
        ├── models.py
        ├── main.py
        └── crawler
            └── scraper_1.py
            └── scraper_2.py
            └── scraper_3.py
    

    models 定义了数据库引擎和会话,我想让 scraper 能够与数据库交互,但是
    from ..models import func 会得到报错
    ImportError: attempted relative import with no known parent package

    from app.models import func 又会得到报错
    ModuleNotFoundError: No module named 'app'

    网上提到的在 app 目录下创建空白 __init__.py 似乎也没有效果,而 GPT 提到的将 app 目录添加到环境变量是最佳做法吗?还是说我这样的目录结构本身就是错的?

    第 1 条附言  ·  25 天前
    抱歉没讲清楚,目前同目录下的 main 是能够识别并导入 models 的。我是想要 scraper 去调用 models 进行数据库交互,暂时还不需要 main
    我现在临时的解决方法是把 models 移动到 crawler 目录下 hhh
    23 条回复    2024-08-24 10:56:28 +08:00
    mickerwx
        1
    mickerwx  
       26 天前   ❤️ 1
    把你原来的导入删掉 然后在 scraper 里面写上 models 的模型名称,这个时候模型名称会报错,你把鼠标放上去,下面就会出现 import.... 点击一下 就可以了 前提你用的是 pycharm
    yanghanlin
        2
    yanghanlin  
       26 天前 via Android   ❤️ 1
    试试 python -m app.main 而非 python app/main.py
    ztm0929
        3
    ztm0929  
    OP
       26 天前
    @yanghanlin hhh 忘记说了,同目录下的 main 是可以导入 models 并成功识别的,不过-m 选项确实网上也有提到,我再试试看
    ztm0929
        4
    ztm0929  
    OP
       26 天前
    @mickerwx 只用 VSCode 哈哈哈,不过我也试试这个方法~
    NoOneNoBody
        5
    NoOneNoBody  
       26 天前   ❤️ 1
    给个我自用的方案,但我没编译过,不知道编译时会否出错
    在项目每个有 py 的子目录,都放一个空的,0 字节的 __init__.py ,项目根目录不需要
    然后,所有 import 都写为 from 一级子目录.二级子目录.xxx import ...,即使是内层的 py 也是这样写,总之就是从第一级开始写

    例如你这个,app 视为项目根目录,scraper_1.py 里面 import scraper_2 ,就要写成 from crawler import scraper_2 ,或者 import crawler.scraper_2 as ...,就是不要理会在哪一级或者是否同级,都要从第一级开始写 namespace
    然后,所有入口程序都应该放在项目根目录,你这个就是 app 这个目录。如果你想直接运行 scraper_1.py ,也要在 app 内另写一个 run_scraper_1.py 把 scraper_1 导入来运行
    darksword21
        6
    darksword21  
       26 天前 via iPhone   ❤️ 1
    官方文档 package and module
    assassing
        7
    assassing  
       26 天前   ❤️ 1
    你需要在 app/__init__.py 中手动打入文件中的函数,例如:from .models.py import func ,注意文件名前面的点。然后在 scraper_1.py 中就能直接导入了:from app import func
    cnt2ex
        8
    cnt2ex  
       26 天前   ❤️ 2
    python 会把被执行的脚本所在的目录插入到 sys.path 中,所以 import 都是相对于脚本所在位置计算的,所以一般入口脚本都是在项目根目录,其他东西都是相对于项目根目录。

    如果你不想把入口脚本放在根目录,就利用 PYTHONPATH ,在里面加上项目根目录。

    比如 PYTHONPATH=/whatever/comes/before/xxx_project/app python main.py

    这样你的 import 都可以写成
    ```
    from models import something
    from crawler.scrapper_1 import something
    ```

    而不管 main.py 在哪个位置
    forQ
        9
    forQ  
       26 天前   ❤️ 1
    sys.path.append()

    sys.path.insert()
    chenqh
        10
    chenqh  
       26 天前   ❤️ 1
    你把 main 移到 app 同层,就可以 from app.models import func
    NickLuan
        11
    NickLuan  
       26 天前   ❤️ 1
    总结的到位👍
    @cnt2ex
    Sawyerhou
        12
    Sawyerhou  
       26 天前   ❤️ 1
    楼上几层的观点+1 ,

    现有目录结构可以试试
    from models import func

    如果想从 app 导入,就要 append 路径到 sys ,
    不然 main 函数找不到 app
    y1y1
        13
    y1y1  
       26 天前   ❤️ 1
    import 的根目录是入口文件的
    kanchi240
        14
    kanchi240  
       26 天前   ❤️ 1
    https://docs.python.org/3/tutorial/modules.html#intra-package-references
    Since the name of the main module is always "__main__", modules intended for use as the main module of a Python application must always use absolute imports.
    sujin190
        15
    sujin190  
       25 天前 via Android   ❤️ 1
    有__init__.py 文件的文件夹才是 python 的 package ,否则就是一个普通文件夹,而且如果你的 scraper 是个包含 main 的执行程序,此时是不能导入 models 的,因为不在 package 导入路径里,python import 的是 package 不是目录或者文件
    EndlessMemory
        16
    EndlessMemory  
       25 天前   ❤️ 1
    添加当前路径到环境变量
    houzhiqiang
        17
    houzhiqiang  
       25 天前
    根本原因是你要找到你的程序入口
    houzhiqiang
        18
    houzhiqiang  
       25 天前   ❤️ 1
    run.py # from app import run_app
    |----app
    |----models.py
    |----__init__.py # def run_app
    |----crawler
    |---- a.py # from ..models import func

    $ python run.py

    |----app
    |----__main__.py # from . import run_app
    |----__init__.py # def run_app
    |----models.py
    |----crawler
    |----a.py # from ..models import func

    $ python -m app
    Maerd
        19
    Maerd  
       25 天前
    楼上的很多都没说到点上,如果你是 pycharm ,可以不用配置,如果你是 vscode,需要将 PYTHONPATH 设为源代码根目录
    houzhiqiang
        20
    houzhiqiang  
       25 天前   ❤️ 1
    @Maerd python xxx.py 会自动把当前目录加入 sys.path ,只需要正确找到顶层包就可以正确写出相对和绝对 import 的路径
    sys.path 第一个元素的值为
    python x.py # '.'
    python -m x.x # '.'
    python x/x.py # './x'
    ztm0929
        21
    ztm0929  
    OP
       25 天前 via iPhone
    @Maerd 咦😕原来不同编辑器还有这个问题呀
    volvo007
        22
    volvo007  
       25 天前
    看看这个有没有帮助 https://www.bilibili.com/video/BV1K24y1k7XA/? spm_id_from=333.999.0.0&vd_source=bcfa969247c2baeec790cdac234b36e6
    marvyn
        23
    marvyn  
       23 天前
    在对应环境的 site-packages 下新增一个 app.pth ,里面内容就是一行
    比如 D:\xxx_project\app

    导入时:
    from app.models import func
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   997 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 19:01 · PVG 03:01 · LAX 12:01 · JFK 15:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.