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

187 天前
ztm0929  ztm0929

我是编程新手,正在练习爬虫项目,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 目录添加到环境变量是最佳做法吗?还是说我这样的目录结构本身就是错的?

5832 次点击
所在节点   Python  Python
23 条回复
mickerwx
mickerwx
186 天前
把你原来的导入删掉 然后在 scraper 里面写上 models 的模型名称,这个时候模型名称会报错,你把鼠标放上去,下面就会出现 import.... 点击一下 就可以了 前提你用的是 pycharm
yanghanlin
yanghanlin
186 天前
试试 python -m app.main 而非 python app/main.py
ztm0929
ztm0929
186 天前
@yanghanlin hhh 忘记说了,同目录下的 main 是可以导入 models 并成功识别的,不过-m 选项确实网上也有提到,我再试试看
ztm0929
ztm0929
186 天前
@mickerwx 只用 VSCode 哈哈哈,不过我也试试这个方法~
NoOneNoBody
NoOneNoBody
186 天前
给个我自用的方案,但我没编译过,不知道编译时会否出错
在项目每个有 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
darksword21
186 天前
官方文档 package and module
assassing
assassing
186 天前
你需要在 app/__init__.py 中手动打入文件中的函数,例如:from .models.py import func ,注意文件名前面的点。然后在 scraper_1.py 中就能直接导入了:from app import func
cnt2ex
cnt2ex
186 天前
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
forQ
186 天前
sys.path.append()

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

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

如果想从 app 导入,就要 append 路径到 sys ,
不然 main 函数找不到 app
y1y1
186 天前
import 的根目录是入口文件的
kanchi240
186 天前
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
186 天前
有__init__.py 文件的文件夹才是 python 的 package ,否则就是一个普通文件夹,而且如果你的 scraper 是个包含 main 的执行程序,此时是不能导入 models 的,因为不在 package 导入路径里,python import 的是 package 不是目录或者文件
EndlessMemory
186 天前
添加当前路径到环境变量
houzhiqiang
186 天前
根本原因是你要找到你的程序入口
houzhiqiang
186 天前
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
186 天前
楼上的很多都没说到点上,如果你是 pycharm ,可以不用配置,如果你是 vscode,需要将 PYTHONPATH 设为源代码根目录
houzhiqiang
186 天前
@Maerd python xxx.py 会自动把当前目录加入 sys.path ,只需要正确找到顶层包就可以正确写出相对和绝对 import 的路径
sys.path 第一个元素的值为
python x.py # '.'
python -m x.x # '.'
python x/x.py # './x'

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1066810

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX