Python笔记之项目依赖管理与模块运行机制详解:从 requirements.txt 到 python -m 和包入口

📅 2026/7/1 18:14:40
Python笔记之项目依赖管理与模块运行机制详解:从 requirements.txt 到 python -m 和包入口
Python笔记之项目依赖管理与模块运行机制详解从 requirements.txt 到 python -m 和包入口code review!文章目录Python笔记之项目依赖管理与模块运行机制详解从 requirements.txt 到 python -m 和包入口1.1 requirements.txt 是什么1.2 常见版本写法1.3 生成 requirements.txt1.4 根据 requirements.txt 安装依赖1.5 使用虚拟环境管理依赖1.6 pip freeze 的局限1.7 其他依赖管理工具2. pip install 与 python -m pip install2.1 二者的共同点2.2 pip install 的执行方式2.3 python -m pip install 的执行方式2.4 为什么推荐 python -m pip2.5 Linux/macOS 上避免滥用 sudo pip2.6 总结建议3. python -m 的含义和用法3.1 python -m 是什么3.2 python -m 如何查找模块3.3 常见使用场景3.3.1 使用当前 Python 环境安装包3.3.2 创建虚拟环境3.3.3 启动临时 HTTP 文件服务器3.3.4 运行代码格式化工具3.4 只输入 python -m 会怎样4. Python 脚本入口if __name__ __main__4.1 它的作用4.2 __name__ 是什么4.3 不使用入口判断的问题4.4 使用入口判断解决问题4.5 推荐写法封装 main() 函数5. Python 包中的 __init__.py 和 __main__.py5.1 二者的核心区别5.2 __init__.py 的作用5.3 __main__.py 的作用5.4 __init__.py 和 __main__.py 的触发时机6. 相对导入from .core import start_game6.1 .core 中的点是什么意思6.2 为什么不直接写 from core import start_game6.3 一个点和两个点的区别7. 如何运行包python -m my_game7.1 正确启动方式7.2 python -m my_game 背后的过程7.3 不推荐直接运行 __main__.py7.4 推荐项目结构8. 整体关系总结8.1 依赖安装层面8.2 命令执行层面8.3 单文件脚本层面8.4 包结构层面1.1requirements.txt是什么在 Python 项目中requirements.txt是一个常见的依赖声明文件用来记录项目需要安装的第三方 Python 包。它的核心作用是让不同环境安装一致的依赖例如本地电脑、同事电脑、测试服务器、生产服务器等。常见内容如下flask3.0.2 requests2.31.0 numpy1.26.4 pandas1.2 常见版本写法flask3.0.2表示精确锁定版本推荐在生产环境中使用。requests2.31.0表示安装大于或等于指定版本的包。numpy1.26.4表示安装小于或等于指定版本的包。pandas表示安装最新可用版本。生产环境中不推荐不写版本号因为未来包更新后可能导致项目行为变化。1.3 生成requirements.txt在当前 Python 环境中导出已安装的包pip freezerequirements.txt更推荐写法python-mpip freezerequirements.txt需要注意pip freeze会导出当前环境中的所有已安装包。如果没有使用虚拟环境它可能会把很多与当前项目无关的包也导出。1.4 根据requirements.txt安装依赖在新环境中安装依赖pipinstall-rrequirements.txt更推荐写法python-mpipinstall-rrequirements.txtpip会读取requirements.txt中的每一行并安装对应的包。1.5 使用虚拟环境管理依赖在正式项目中强烈建议先创建并激活虚拟环境再安装依赖。创建虚拟环境python-mvenv venvWindows 激活虚拟环境venv\Scripts\activatemacOS / Linux 激活虚拟环境sourcevenv/bin/activate激活后再安装依赖python-mpipinstallflask requests然后导出python-mpip freezerequirements.txt这样生成的requirements.txt会更干净只包含当前虚拟环境里的依赖。1.6pip freeze的局限pip freeze会把直接依赖和间接依赖全部导出。例如你只安装了flask但 Flask 依赖的Werkzeug、Jinja2、click等包也会被一起写进requirements.txt。这有好处也有缺点。好处是环境可复现性强。缺点是文件会变长并且不容易看出哪些包是项目主动依赖的。1.7 其他依赖管理工具如果想只根据项目代码中的import语句生成依赖文件可以了解pipreqspipreqs.--force如果项目更复杂也可以使用现代依赖管理工具例如 Poetry、Pipenv 等。它们通常使用pyproject.toml或专门的锁文件来管理依赖比传统requirements.txt更适合大型项目。2.pip install与python -m pip install2.1 二者的共同点在很多简单场景下pipinstallrequests和python-mpipinstallrequests都可以安装第三方包。但它们定位 Python 环境的方式不同。2.2pip install的执行方式当执行pipinstallrequests系统会在环境变量PATH中查找名为pip的可执行文件。如果电脑上只有一个 Python并且环境配置正确通常没有问题。但如果存在多个 Python 版本、多个虚拟环境或者系统 Python 与项目 Python 并存就可能调用到错误的pip。例如电脑里同时有 Python 3.10 和 Python 3.12pipinstallrequests这个命令不一定会把requests安装到你实际运行项目的那个 Python 环境里。2.3python -m pip install的执行方式当执行python-mpipinstallrequests含义是使用当前这个python解释器运行它环境中的pip模块并安装包。也就是说包会安装到当前python对应的环境中。如果你明确使用 Python 3.12可以写python3.12-mpipinstallrequests这样可以更明确地把包安装到 Python 3.12 对应的环境中。2.4 为什么推荐python -m pip推荐使用python-mpipinstall包名主要原因有三个。第一可以避免多个 Python 环境导致的安装错位。第二适合写进文档和自动化脚本可读性和确定性更高。第三在某些系统上更新pip自身时更稳妥。例如更新pippython-mpipinstall--upgradepip比直接执行下面命令更推荐pipinstall--upgradepip2.5 Linux/macOS 上避免滥用sudo pip不推荐这样安装包sudopipinstallrequests这样可能会污染系统 Python 环境影响系统工具或其他项目。更安全的做法是使用虚拟环境python-mvenv venvsourcevenv/bin/activate python-mpipinstallrequests如果确实要安装到当前用户目录可以使用python-mpipinstall--userrequests2.6 总结建议命令推荐程度说明pip install 包名一般推荐在已经明确激活虚拟环境时可以使用python -m pip install 包名更推荐更能确保包安装到当前 Python 对应的环境中python3.12 -m pip install 包名多版本场景推荐明确指定 Python 版本sudo pip install 包名不推荐容易污染系统 Python 环境当遇到明明安装了包但运行代码仍然报错ModuleNotFoundError可以优先检查是否安装到了错误的 Python 环境中并尝试使用python-mpipinstall包名3.python -m的含义和用法3.1python -m是什么python -m的作用是把一个模块或包当作脚本运行。基本语法是python-m模块名或者python-m包名这里的-m可以理解为module表示后面跟的是模块名或包名而不是普通的文件路径。3.2python -m如何查找模块执行python-mpip时Python 会在当前解释器的模块搜索路径中查找pip模块。这个搜索路径通常包括当前工作目录Python 标准库当前环境中安装的第三方包其他sys.path中的路径找到对应模块后Python 会以脚本方式运行它。3.3 常见使用场景3.3.1 使用当前 Python 环境安装包python-mpipinstallrequests这表示使用当前 Python 解释器对应的pip安装requests。3.3.2 创建虚拟环境python-mvenv venv这表示运行 Python 标准库中的venv模块在当前目录创建一个名为venv的虚拟环境。3.3.3 启动临时 HTTP 文件服务器python-mhttp.server8000这会在当前目录启动一个简单的 HTTP 服务。访问地址通常是http://localhost:80003.3.4 运行代码格式化工具如果安装了black可以这样运行python-mblack myscript.py3.4 只输入python -m会怎样如果只执行python-m而不提供模块名Python 会报错因为-m后面必须跟模块或包名。正确格式是python-m模块名例如python-mpip--version4. Python 脚本入口if __name__ __main__4.1 它的作用if __name__ __main__:是 Python 中常见的程序入口判断。它的作用是让一个 Python 文件既可以被直接运行也可以被其他文件导入并且在导入时不会执行某些只应该在直接运行时执行的代码。4.2__name__是什么每个 Python 文件在运行时解释器都会自动提供一个变量__name__它的值取决于当前文件的运行方式。如果文件被直接运行python calculator.py那么该文件中的__name__值是__main__如果文件被其他文件导入importcalculator那么calculator.py中的__name__值通常是calculator也就是模块名。4.3 不使用入口判断的问题假设有一个文件calculator.pydefadd(a,b):returnabprint(正在测试 add 函数...)print(add(2,3))直接运行它python calculator.py输出测试信息是正常的。但是如果另一个文件main.py导入它importcalculatorprint(calculator.add(5,5))运行python main.py会发现calculator.py中的测试代码也被执行了。原因是import calculator会执行calculator.py中的顶层代码。4.4 使用入口判断解决问题可以改成defadd(a,b):returnabif__name____main__:print(正在测试 add 函数...)print(add(2,3))这样直接运行calculator.py时python calculator.py__name__等于__main__测试代码会执行。被其他文件导入时importcalculator__name__等于calculator测试代码不会执行。4.5 推荐写法封装main()函数更推荐把直接运行时要执行的逻辑放进main()函数中defmy_awesome_function():print(核心逻辑)defmain():print(程序开始运行...)my_awesome_function()if__name____main__:main()这种结构更清晰也方便后续维护、测试和复用。5. Python 包中的__init__.py和__main__.py5.1 二者的核心区别__init__.py和__main__.py都是 Python 包中具有特殊含义的文件但作用不同。简单来说__init__.py 负责包的初始化 __main__.py 负责包被直接运行时的入口5.2__init__.py的作用__init__.py通常放在包目录下。例如my_game/ ├── __init__.py ├── core.py └── utils.py它的主要作用包括标识这个目录是一个 Python 包在包第一次被导入时执行初始化代码简化外部导入路径控制from package import *的导出内容在现代 Python 中即使没有__init__.py某些目录也可以作为命名空间包被导入。但在普通项目中仍然建议为常规包添加__init__.py这样结构更清晰也更兼容。示例# my_game/__init__.py__version__1.0.0from.coreimportstart_game这样外部可以写frommy_gameimportstart_game而不必写frommy_game.coreimportstart_game5.3__main__.py的作用__main__.py用来让一个包可以被直接运行。如果包结构如下my_game/ ├── __init__.py ├── __main__.py ├── core.py └── utils.py当执行python-mmy_gamePython 会查找并执行my_game/__main__.py因此__main__.py通常放启动逻辑例如命令行入口、服务启动入口等。示例# my_game/__main__.pyfrom.coreimportstart_gamedefmain():print(游戏正在通过命令行启动...)start_game()if__name____main__:main()对应的核心逻辑文件# my_game/core.pydefstart_game():print(游戏开始)5.4__init__.py和__main__.py的触发时机文件作用触发时机__init__.py初始化包当包被import时执行__main__.py包的运行入口当执行python -m 包名时执行示例importmy_game会触发my_game/__init__.py执行python-mmy_game会触发my_game/__main__.py需要注意的是执行python -m my_game时Python 通常也会先加载包因此__init__.py中的代码也可能先执行然后再执行__main__.py。所以不建议在__init__.py中放复杂逻辑或有副作用的代码。6. 相对导入from .core import start_game6.1.core中的点是什么意思在from.coreimportstart_game中.core表示相对导入。这里的点.表示当前包。所以from.coreimportstart_game意思是从当前包中的core.py文件里导入start_game。如果项目结构是my_game/ ├── __init__.py ├── __main__.py └── core.py那么在my_game/__main__.py中from.coreimportstart_game表示从my_game/core.py导入start_game6.2 为什么不直接写from core import start_game不建议在包内部写fromcoreimportstart_game因为这是绝对导入。Python 会在模块搜索路径中查找名为core的模块而不一定是当前包里的core.py。如果环境中刚好存在其他名为core的模块就可能导入错误。使用相对导入from.coreimportstart_game可以明确表示从当前包内部导入。6.3 一个点和两个点的区别一个点from.coreimportstart_game表示当前包。两个点from..commonimporthelper表示上一级包。三个点from...utilsimporttool表示上上一级包。相对导入通常用于包内部模块之间的引用。7. 如何运行包python -m my_game7.1 正确启动方式如果项目结构如下project/ └── my_game/ ├── __init__.py ├── __main__.py ├── core.py └── utils.py应该在project目录下执行python-mmy_game注意不是进入my_game目录里面执行而是在它的上一级目录执行。7.2python -m my_game背后的过程执行python-mmy_game时大致过程如下Python 在当前目录和sys.path中查找名为my_game的包找到my_game目录识别它是一个包加载包查找并执行my_game/__main__.py因此如果想让一个包支持python-m包名包中需要有__main__.py7.3 不推荐直接运行__main__.py不推荐这样运行python my_game/__main__.py也不推荐进入包目录后运行python __main__.py原因是这样 Python 会把__main__.py当成一个普通脚本执行而不是作为my_game包的一部分执行。此时类似下面的相对导入可能会失败from.coreimportstart_game常见错误是ImportError: attempted relative import with no known parent package因为相对导入依赖包上下文而直接运行单个文件时Python 不知道它属于哪个父包。7.4 推荐项目结构一个较规范的包结构可以写成project/ └── my_game/ ├── __init__.py ├── __main__.py ├── core.py └── utils.pycore.pydefstart_game():print(游戏开始)__main__.pyfrom.coreimportstart_gamedefmain():print(游戏正在通过命令行启动...)start_game()if__name____main__:main()在project目录下运行python-mmy_game输出游戏正在通过命令行启动... 游戏开始8. 整体关系总结8.1 依赖安装层面requirements.txt负责记录项目依赖。推荐用虚拟环境隔离项目依赖python-mvenv venv安装依赖推荐使用python-mpipinstall包名导出依赖python-mpip freezerequirements.txt安装依赖文件python-mpipinstall-rrequirements.txt8.2 命令执行层面python -m表示把模块或包作为脚本运行。例如python-mpipinstallrequests python-mvenv venv python-mhttp.server8000python-mmy_game8.3 单文件脚本层面如果一个文件既要能直接运行又要能被导入复用推荐写defmain():passif__name____main__:main()8.4 包结构层面__init__.py负责包初始化。__main__.py负责包作为程序运行时的入口。相对导入from.coreimportstart_game表示从当前包内部的core.py导入start_game。运行整个包python-mmy_game而不是python my_game/__main__.py这样可以保证包上下文正确相对导入也能正常工作。