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

92 天前
 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 目录添加到环境变量是最佳做法吗?还是说我这样的目录结构本身就是错的?

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

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

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

如果想从 app 导入,就要 append 路径到 sys ,
不然 main 函数找不到 app
y1y1
92 天前
import 的根目录是入口文件的
kanchi240
91 天前
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
91 天前
有__init__.py 文件的文件夹才是 python 的 package ,否则就是一个普通文件夹,而且如果你的 scraper 是个包含 main 的执行程序,此时是不能导入 models 的,因为不在 package 导入路径里,python import 的是 package 不是目录或者文件
EndlessMemory
91 天前
添加当前路径到环境变量
houzhiqiang
91 天前
根本原因是你要找到你的程序入口
houzhiqiang
91 天前
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
91 天前
楼上的很多都没说到点上,如果你是 pycharm ,可以不用配置,如果你是 vscode,需要将 PYTHONPATH 设为源代码根目录
houzhiqiang
91 天前
@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