python 导包相对路径与项目打包wheel

本文详细探讨了Python中模块和包的概念,包括它们的定义、目录结构以及如何通过相对和绝对导入来组织代码。作者通过实例解释了在创建包时__init__.py的作用,并展示了如何处理相对导入的问题。文章还介绍了将代码打包成wheel文件的步骤,强调了在打包过程中使用相对路径的重要性,并给出了setup.py的配置示例。最后,作者建议在开发时尽量使用相对路径,以便于代码作为第三方包使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天想将自己的代码打包成一个wheel文件,然后pip安装一键搞定的事情。遇到了相对导包的问题。要彻底地解决这个问题需要重新认识一下python关于模块与包的相关概念。
一、什么是模块?
一个py文件就是一个模块
二、什么是包:
多个模块放在一个目录下,并且存在一个__init__.py文件的目录就是一个模块。
首先构建一个测试的项目,目录结构如下:
在这里插入图片描述
代码如下:

# hello.__init__.py
print('__package__={} | __file__={} | __name__={} '.format(__package__, __file__, __name__))
# hello.utils.py
print('__package__={} | __file__={} | __name__={} '.format(__package__, __file__, __name__))
def say(something):
    print(something)
# hello.doing.py
import sys
from utils import say

def greet():
    say('hello world')
    
if __name__ == '__main__':
    print(sys.path)
    greet()
# main.py
import sys
# sys.path.append('./hello')
from hello.doing import greet
if __name__ == '__main__':
	print(sys.path)
    greet()

在执行python doing.py的脚本时,输出如下:
在这里插入图片描述

从上面打印的结果可知,hello.init.py并没有执行,doing.py导入utils模块时,输出了utils.py相关的内容。从sys.path的路径来看,我们可以发现,python会默认把当前文件夹test/hello添加到系统变量的路径当中,那么系统变量路径+当前模块的名称__name__,即可找到该模块,此种用法是最为常规的执行方式。
当我们尝试将doing.py中的from utils import say修改成from .utils import say,此时执行脚本,输出如下:
在这里插入图片描述
这就意味着系统没法找到包的路径,原因很简单系统路径./test/hello/拼接上.utils,确实没有这号人物啊。那如果我非要from .utils import say如此操作呢?在模块之间的调用是无法实现的。那相对路径总得有啥作用吧?再看我们的main.py的模块,此时我们执行python main.py ,输出如下:

__package__=hello | __file__=/Users/xxx/workspace/test/hello/__init__.py | __name__=hello 
['/Users/xxx/workspace/test', '/Users/xxx/workspace/test', '/Users/xxx/.pyenv/versions/3.6.5/lib/python36.zip', '/Users/xxx/.pyenv/versions/3.6.5/lib/python3.6', '/Users/xxx/.pyenv/versions/3.6.5/lib/python3.6/lib-dynload', '/Users/xxx/.virtualenvs/test/lib/python3.6/site-packages']

__package__=hello | __file__=/Users/xxx/workspace/test/hello/utils.py | __name__=hello.utils 
hello world

可以看出,尽管我们没有调用hello.init.py,系统也默认执行了一次,此时sys.path中没有了test/hello的目录,但是依旧能调用到utils,此时将代码改回from utils import say,输出如下:
在这里插入图片描述
原因就是系统路径没有hello与之拼接?于是在main.py导入hello.doing之前加上sys.path.append('./hello'),再次运行就正常了。
看到这里,我们应该明白了,python提供了两种互补的方式进行导包,如果是包之间的导入,那么包内模块之间需要相对路径,所以即使你把包放到lib/python3.6/site-packages的目录下也能正常的导入,但如果是模块之间则需要绝对路径。包与模块之间的界限,就是是否是目录以及目录存在一个__init__,但是我删掉__inti__也如期正常运行。其实这个__init__是为了初始化模块的导入,在python中第一次导入之后就不在重复导入,后面的导入只是计数增加而已。也就是说__init__可以用来管理包目录下的所以模块的初始化,比如flask的init中就会一相对路径的形式把自己的模块一一进行初始化。
终于搞清楚了这个流程,所以如果你的代码要作为第三方包,给别人pip install之后调用,那么你的代码内部之间就需要用相对路径。我尝试去看了很多的模块的源码的目录结构,比如flask、numpy等都是如此。
使用相对路径的好处就是无论用户放在那个路径下,我们都可以通过指定添加sys.path来进行获取,所以以后开发还是尽量地使用相对路径吧,毕竟哪天说不准就要提供包了。关于项目打包wheel,等实践完成再补充了。

明白了上述的过程,打包wheel的流程就简单多了。
1.首先hello必须是一个包,即必须有__init__.py
2.在hello同级目录下,新建setup.py的代码如下:

from setuptools import find_packages, setup

setup(
    name='hello',
    version='1.0.0',
    packages=find_packages(),
    include_package_data=True,
    zip_safe=False,
    py_modules=['hello'],
    # install_requires=[
    #     'flask',
    # ],
)

name是生成的wheel的名称,py_modules是你的包名,可以多个包一起打包成一个wheel模块。具体参数查询setuptools模块的使用文档即可。执行:

# 生成.tar.gz的压缩包
python setup.py sdist 
# 生成wheel的压缩包
python setup.py bdist_wheel

执行上述命令之后会在当前目录下生成dist的目录,目标文件就在那里,pip install 即可使用。

参考:https://blog.youkuaiyun.com/weixin_39523998/article/details/78040768?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.control
官方文档:https://docs.python.org/zh-cn/3/reference/import.html#packages

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值