Python module 的相对导入

news/2024/9/22 13:27:54

Python module 的相对导入

python module relative import

目录
  • Python module 的相对导入
  • 项目结构
    • main.py
    • pet.py
    • cat.py
    • dog.py
    • pet/__init__.py
  • ModuleNotFoundError: No module named 'pet'
    • solution 1
    • solution 2
  • ImportError: attempted relative import with no known parent package
    • solution
  • 总结

Created: 2024-09-22T11:16+08:00
Published: 2024-09-22T13:20+08:00

Category: Python

第一次编写 Python package 时候,很可能会需要不同文件夹下的 python 文件相互 import。
如果不了解 Python 的 relative import 机制,很可能会导致如下两种错误:

ModuleNotFoundError: No module named 'xxx'
ImportError: attempted relative import with no known parent package

本文通过一个 petstore 例子,告诉大家如何写 package。

有四个需求:

  1. Cat 和 Dog 两个类,继承自 Pet 类
  2. 我们需要在 main.py 中自定义类继承 Pet
  3. 我们需要在 main.py 中使用 Cat
  4. cat.py 里面有一个 if __name__ == "__main__,我们可以直接执行 cat.py

项目结构

├─main.py
│
└─petstore│  __init__.py│├─pet│  │  cat.py│  │  dog.py│  │  pet.py│  └─ __init__.py│└─store__init__.py

main.py

# main.pyprint("we are running: ", __name__)import sysfrom petstore.pet import Pet
from petstore.pet.cat import Catclass Bird(Pet):def __init__(self) -> None:super().__init__()def eat(self):print("bird eat")if __name__ == "__main__":c = Cat()c.eat()b = Bird()b.eat()

pet.py

# pet.pyimport abcclass Pet(abc.ABC):def __init__(self) -> None:pass@abc.abstractmethoddef eat(self):pass

cat.py

# cat.py
print("importing cat")
print("cat.py.__name__:", __name__)
print("done\n")from .pet import Pet # <- attention here!!!class Cat(Pet):def __init__(self) -> None:passdef eat(self):print("cat eat")if __name__ == "__main__":c = Cat()c.eat()

dog.py

print("importing dog")
print("dog.py.__name__:", __name__)
print("done\n")from .pet import Petclass Dog(Pet):def __init__(self) -> None:passdef eat(self):print("dog eat")

pet/__init__.py

"""
这个包处理宠物管理功能。
"""from .pet import Pet
from .cat import Cat
from .dog import Dog__all__ = ['Pet', 'Cat', 'Dog']

ModuleNotFoundError: No module named 'pet'

如果将 from .pet import Pet 写作 from pet import Pet
并且直接在项目 根目录 python ./main.py,就会报这个错。

因为 python 文件直接作为命令行参数执行的时候,
解释器就认为其是一个 module 的 root[1]没有传递任何关于 petstore 的 package structure
当执行到 cat.py 中的 from pet import Pet 时候,
就会从 sys.path 中查找 pet,目前的路径是根目录,没有 pet,所以找不到就报错。

solution 1

如果这时候,我们在 main.py 中加上:

import sys
sys.path.append("./petstore/pet")

就可以让 cat.pyfrom pet import pet 生效。

solution 2

直接使用 sys.path 毕竟不好,将路径 hard encode 到了文件中,
更好的写法是写作 from .pet import Pet
这样 python main.py 时候,可以正确解析 petstore package 的结构。

ImportError: attempted relative import with no known parent package

这个错误发生在使用了 relative import 后,
直接运行使用 relative import 的 python 文件。
python .\petstore\pet\cat.py

python .\petstore\pet\cat.py
importing cat
cat.py.**name**: **main**
doneTraceback (most recent call last):
File "~\petstore\pet\cat.py", line 5, in <module>
from .pet import Pet
ImportError: attempted relative import with no known parent package

因为直接运行的文件将视作 package 的 root,所以没法 relative import。

solution

想要直接运行 cat.py,可以使用命令 python -m petstore.pet.cat

关键是参数 -m

> python -m petstore.pet.catimporting cat
cat.py.__name__: petstore.pet.cat
doneimporting dog
dog.py.__name__: petstore.pet.dog
done<frozen runpy>:128: RuntimeWarning: 'petstore.pet.cat' found in sys.modules after import of package 'petstore.pet', but prior to execution of 'petstore.pet.cat'; this may result in unpredictable behaviour
importing cat
cat.py.__name__: __main__
donecat eat

可以看到 petstore.pet.cat 这个 module 被 解析了两次,第一次其 __name__petstore.pet.cat,第二次为 __main__

总之,不应该在 module 内部运行 script[2]

总结

那么正确的做法应该是什么呢?

在同一个 package 中,只使用 relative import
要测试 package 中的 module,在外部进行测试,module 内不要有 if __name__ == "__main__"

复述下需求的解决方法:

├─main.py
│
└─petstore│  __init__.py│├─pet│  │  cat.py│  │  dog.py│  │  pet.py│  └─ __init__.py│└─store__init__.py
  1. Cat 和 Dog 两个类,继承自 Pet 类
    使用 from .pet import Pet
  2. 我们需要在 main.py 中自定义类继承 Pet
    将 petstore 视作一个 package,直接 from petstore.pet import Pet
  3. 我们需要在 main.py 中使用 Cat
    将 petstore 视作一个 package,直接 from petstore.pet.cat import Cat
  4. cat.py 里面有一个 if __name__ == "__main__,我们可以直接执行 cat.py
    因为 cat.py 中已经使用了 relative import 所以使用命令 python -m petstore.pet.cat 来运行

  1. https://stackoverflow.com/a/73149/20752995 ↩︎

  2. https://stackoverflow.com/a/8195271/20752995 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/63320.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Nuxt Kit API :路径解析工具

title: Nuxt Kit API :路径解析工具 date: 2024/9/22 updated: 2024/9/22 author: cmdragon excerpt: 摘要:本文介绍了Nuxt Kit中用于解析路径的API工具,包括resolvePath、resolveAlias、findPath和createResolver。这些工具助力开发者处理模块路径、别名、文件扩展名,提…

使用GPU 加速 Polars:高效解决大规模数据问题

Polars 最近新开发了一个可以支持 GPU 加速计算的执行引擎。这个引擎可以对超过 100GB 的数据进行交互式操作能。本文将详细讨论 Polars 中DF的概念、GPU 加速如何与 Polars DF协同工作,以及使用新的 CUDA 驱动执行引擎可能带来的性能提升。 https://avoid.overfit.cn/post/b…

我的网站集成ElasticSearch初体验

最近,我给我的网站(https://www.xiandanplay.com/)尝试集成了一下es来实现我的一个搜索功能,因为这个是我第一次了解运用elastic,所以如果有不对的地方,大家可以指出来,话不多说,先看看我的一个大致流程 这里我采用的sdk的版本是Elastic.Clients.Elasticsearch, Ver…

Flipper Zero极客的便携式多功能工具设备

官网:Flipper Zero — 极客的便携式多功能工具设备 Flipper Zero是近两年比较热门的硬件工具,官方固件主要涵盖的功能为Sub-Ghz,125kHz,NFC,红外。 基本信息资料都可以在官方网站找到比较详细的文档解释。本篇主要是一个基础入门,这系列也是给自己学习此硬件一个上手研究…

C盘扩容免费工具

1.diskgenius 下载 https://www.diskgenius.cn/download.php 解压即可使用,无需安装 2.下载 安装Windows_PE环境 https://www.diskgenius.cn/help/windows_aik_adk_installnotes.php?Version=0A000000&Build=22631&Lang=936 官方软件,安全五毒 3.运行diskgenius ,点…

Leetcode 2464. 有效分割中的最少子数组数目

1.题目基本信息 1.1.题目描述 给定一个整数数组 nums。 如果要将整数数组 nums 拆分为 子数组 后是 有效的,则必须满足: 每个子数组的第一个和最后一个元素的最大公约数 大于 1,且 nums 的每个元素只属于一个子数组。 返回 nums 的 有效 子数组拆分中的 最少 子数组数目。如果…

debian 系统服务器搭建

在VMWare中安装Debian12.5虚拟机后, 需要开始进行一些设置:1. 先设置网络模式为桥接模式, 这样和主机在同一个局域网,方便后续ssh连接 2. 第1步设置后,重启Debian,登录后, 查看IP和Mac地址, 192.168.31.16,00:0c:29:6c:31:e6 3. 设置路由器,固定IP: 192.168.31.105。 …

初识算法

持续更新数据结构与算法专题,欢迎关注.......1.1 什么是算法? 定义 在数学和计算机科学领域,算法是一系列有限的严谨指令,通常用于解决一类特定问题或执行计算In mathematics and computer science, an algorithm (/ˈlɡərɪəm/) is a finite sequence of rigorous inst…