包和模块是Python语言封装功能和组织程序集的解决方案类似Java中的package和C#中的namespace将固定功能模块的代码聚合在一起提高程序的复用性和可维护性。模块和包在不同环境下位置和作用范围也不一样本文建议和Python全局环境和虚拟环境venv一文搭配食用。1.模块每一个.py文件都是一个模块每个模块中可以包含变量函数类等内容模块多用于封装固定功能的代码每个模块都是一个工具模块可以提升代码的可维护性和可复用性还能避免命名冲突。python中的模块分为三种标准库模块自定义模块和第三方模块标准库模块随着Python自带的一些模块位于Python安装目录的\Lib下site-packages中的除外有些是C语言实现的不能看到源码Pycharm IDE会为我们准备存根文件里面仅有注释也叫内置模块剩下是python实现可见源码叫做非内置模块例如copy, os, math, sys, time等都是标准库模块其中的math, sys, time就是内置模块copy, os就是非内置模块有些模块是用包进行组织的包的概念后面会有介绍python提供了标准库文档用于参考https://docs.python.org/zh-cn/3.14/py-modindex.html自定义模块是我们为了实现功能自己编写的模块第三方模块通常位于Python安装目录的\Lib\site-packages引用别人写好的现成的功能往往使用包来引入通常使用pip进行管理1.1 定义模块模块的命名要符合标识符的命名规则模块名文件名大小写敏感最重要的是不能与标准库模块重名否则引入时会被与之重名的标准库模块顶替类似Java中的双亲委派例如定义两个模块在根路径下order和payorder.pymax_amount 5000_0000 def create_order(): print(create_order) def cancel_order(): print(cancel_order) def info(): print(order info)pay.pytimeout 300 def wechat_pay(): print(wechat_pay) def alipay_pay(): print(alipay_pay) def info(): print(pay info)1.2 引入模块在根目录建一个新的mytest模块引入刚刚建的两个模块总共有5种常见的引入方式在不同的场景使用适合的方式进行导入。1.2.1 import 模块名引入模块中的全部成员要使用模块中的成员需要用模块名.的方式访问import order import pay print(order.max_amount) order.create_order() order.cancel_order() order.info() print(pay.timeout) pay.alipay_pay() pay.wechat_pay() pay.info()1.2.2 import 模块名 as 别名可以为引入的模块取一个别名通过别名.访问但是别名需要符合标识符的命名规范import order as o import pay as p print(o.max_amount) o.create_order() o.cancel_order() o.info() print(p.timeout) p.alipay_pay() p.wechat_pay() p.info()1.2.3 from 模块名 import 具体内容1, 具体内容2 …之前的方式都是将整个模块引入通过from 模块名 import 具体内容,...可以将模块中的部分成员引入并可以不需经过模块直接调用from order import max_amount, create_order, cancel_order, info from pay import timeout, wechat_pay, alipay_pay, info print(max_amount) print(timeout) create_order() cancel_order() alipay_pay() wechat_pay() info()但是有一个问题两个模块中有重名的成员时后引入的会覆盖先引入的例如上面程序运行结果就是info()执行的是pay模块中的info()50000000 300 create_order cancel_order alipay_pay wechat_pay pay info1.2.4 from 模块名 import 具体内容1 as 别名1, 具体内容2 as 别名2 …在3的基础上通过这种方式将重名成员设置别名避免冲突from order import info as o_info from pay import info as p_info o_info() p_info()1.2.5 from 模块名 import *⚠️这是一种不被推荐的用法引入模块中全部成员但是和第1种不同的是访问成员不需要通过模块名或别名同样会出现重名成员后者覆盖前者的情况而且和当前模块中声明的成员也可能无形中发生冲突同样存在按照前后顺序覆盖timeout 0 from order import * from pay import * max_amount 5 print(timeout) print(max_amount) alipay_pay() wechat_pay() create_order() cancel_order() info()运行结果timeout被pay.timeout覆盖order.max_amount也会被max_amount覆盖info()调用的是pay模块的info()300 5 alipay_pay wechat_pay create_order cancel_order pay info在python中可以通过__all__来控制from 模块名 import *引入哪些成员且__all__仅针对from 模块名 import *的方式有效__all__的值可以是列表或元组例如将order.py修改成以下被引入时只能引入create_order, cancel_order两个函数列表和元组中每个元素是字符串形式的属性名不要把函数或变量等直接当成对象直接放进去max_amount 5000_0000 def create_order(): print(create_order) def cancel_order(): print(cancel_order) def info(): print(order info) __all__ [create_order, cancel_order]在mytest.py中再使用未引入的成员将报错from order import * print(max_amount) create_order() cancel_order() info()Traceback (most recent call last): File D:\python-lang-test\test1\mytest.py, line 60, in module print(max_amount) ^^^^^^^^^^ NameError: name max_amount is not defined Process finished with exit code 11.3 主模块和__name__一个python项目由诸多模块构成如果一个模块是直接在python解释器后直接运行的则这个模块就是主模块类似Java中JVM从某个类的public static void main(String[] args)方法开始执行。比如这样运行某个python项目mytest.py模块就是主模块D:\python-lang-test\test1\.venv\Scripts\python.exe D:\python-lang-test\test1\mytest.pypython中有一个特殊的变量__name__是一个字符串类型该变量只有在主模块中出现时才会被python解释器赋值为一个字符串”__main__“如果出现在了非主模块则会被赋值为当前模块的名同时python代码运行时一旦执行了import语句被引入的模块代码就会开始自然从上向下执行类似浏览器中执行js代码一样例如:mytest.py主模块import son print(主模块执行-开始) print(__name__) son.fun()son.pyprint(son模块执行-开始) def fun(): print(__name__)运行结果就是上面说的那样son模块print先执行了然后主模块print后执行而且__name__被解释器自动赋上对应的值son模块执行-开始 主模块执行-开始 __main__ son这样设计的用途是可以对某个模块内自己实现的方法进行简单测试类似Java中如果想在某个类中测试下刚刚写好的方法就会随手就地写一个main方法然后main中直接启动自己写的方法对python而言可以将某个子模块最后加上这样一段if __name__ __main__逻辑print(son模块执行-开始) def fun(): print(hello world) if __name__ __main__: fun()只要将当前模块直接启动就能被当作主模块被解释器直接执行if __name__ __main__下的逻辑实现临时测试但是上线后当作为子模块被主模块引入这段if逻辑则会被忽略如果不加这段if逻辑同样可以进行测试但是需要上线前删除或注释测试代码一旦忘记了或者少注释了一段误上线后就可能造成很大的影响因此if __name__ __main__至少可以使得程序更加安全2.包python中包并不是一个和模块并列的东西而是模块的进一步升级一个包含__init__.py文件的文件夹就叫做包。通常将实现某个近似或相关功能的众多模块放在一个包中。__init__.py是包的初始化文件可以编写一些初始化逻辑比如检查下当前环境等还可以控制包被导入的内容当包被导入时__init__.py将被自动调用模块是对功能的整理包则是对模块的进一步整理一个包中可以包含多个模块也可以包含多个子包包可以提升代码的可维护性和可复用性便于管理大型项目。python的包和模块类似分为标准库包自定义包和第三方包封装标准库模块的自然就是标准库包第三方包和自定义包同理。2.1 定义包定义包和定义模块规则也类似报名符合标识符命名规范不能和标准库包的名称冲突且大小写敏感一般用小写字母。例如在项目根路径下新建一个trade包新建文件夹名字和要建的包的包名一致文件夹里面新建一个空的__init__.py文件就成功创建了一个包存在子包时包名就是父子包用.连接就像Java那样例如org.springframework.bootproject ├── .venv └── trade └── __init__.py在Pycharm IDE中右键新建Python Package可以一气呵成将文件夹和__init__.py同时创建。包中可以新建自己需要的模块例如order.pypay.pyproject ├── .venv └── trade ├── order.py ├── pay.py └── __init__.py2.2 引入包对于包来说有五种和引入模块相似的方式在语法和用法规则都是相同的唯一改变的是模块名前要加上包名模块包import 模块名import 包名.模块名import 模块名 as 别名import 包名.模块名 as 别名from 模块名 import 具体内容1, 具体内容2 …from 包名.模块名 import 具体内容1, 具体内容2 …from 模块名 import 具体内容1 as 别名1, 具体内容2 as 别名2 …from 包名.模块名 import 具体内容1 as 别名1, 具体内容2 as 别名2 …from 模块名 import *from 包名.模块名 import *除了这五种和引入模块相似的语法还有包特有的引入方式新建一个testpg.py模块测试这些方式project ├── .venv ├── testpg.py └── trade ├── order.py ├── pay.py └── __init__.py2.2.1 from 包名 import 模块名testpg.pyfrom trade import pay from trade import order print(pay.timeout) pay.wechat_pay() print(order.max_amount) order.create_order()2.2.2 from 包名 import 模块名 as 别名testpg.pyfrom trade import pay as p from trade import order as o print(p.timeout) p.wechat_pay() print(o.max_amount) o.create_order()2.2.3 from 包名 import *包和模块的import *导入逻辑是不一样的并不是将包下每个模块的所有成员都导入而是和包的__init__.py文件有关__init__.py中定义的内容才能被导入trade/__init__.pyprint(trade init) a 100 b 200testpg.pyfrom trade import * print(a) print(b) print(timeout) print(max_amount)运行结果导入包时打印trade init且只有a b能获取到trade init 100 200 Traceback (most recent call last): File D:\python-lang-test\test1\testpg.py, line 29, in module print(timeout) ^^^^^^^ NameError: name timeout is not defined Process finished with exit code 1如果要通过包引入模块可以在__init__.py文件中直接import模块import也是一种定义trade/__init__.pyprint(trade init) a 100 b 200 import order import paytestpg.pyfrom trade import * print(a) print(b) print(order.max_amount) pay.wechat_pay()还可以通过__all__以字符串指定包中的哪些可以被from 包名 import *的语法引入无需import模块直接写模块名在列表中例如下面程序只有order模块和a b能被引入trade/__init__.pyprint(trade init) a 100 b 200 __all__ [order, a, b]2.2.4 import 包名直接导入包通过包名访问成员导入的包必须在__init__.py中import通过__all__指定在这种引入方式上不生效trade/__init__.pyprint(trade init) import order import pay a 100 b 200testpg.pyimport trade trade.order.create_order() print(trade.a)3.pippip是python自带的第三方包管理器在windows下使用管理员权限打开CMD输入pip回车就能看到提示pip实际上对应的是python安装目录的\Scripts\pip.exe文件第三方包要到pypi的网站查找https://pypi.org/就像从maven中央仓库和npm查找Java或js的第三方依赖那样。通过pip install命令安装第三方包例如pip install numpy全局环境下第三方包和模块会被安装在Python安装目录的\Lib\site-packages一同被安装的还有numpy.libsnumpy-2.3.5.dist-info两个文件夹numpy.libs是该包依赖的一些底层C实现的东西numpy-2.3.5.dist-info里面则是描述文件pip自己也是一个第三方包在安装python环境时一般默认安装pip只要选择了默认安装就会被安装在Lib\site-packagesScripts\pip.exe最终就是在运行Lib\site-packages中的pippypi的服务器在境外夜间可能访问不稳定因此可以使用国内的一些镜像例如清华大学pypi镜像https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simplepip安装时临时指定镜像pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple numpy永久指定镜像⚠️如果在虚拟环境下执行实现每个环境有不同的pip配置虚拟环境目录下要提前创建好一个pip.ini文件例如我的是.venv\pip.inipip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simplepip还有以下几个常见用法命令释义pip list当前环境中已安装的所有第三方包pip config list当前环境pip配置pip uninstall ...从当前环境卸载指定的第三方包pip config unset global.index-url恢复默认的pypi地址4.uvuv是一款基于Rust语言编写的高性能的python包和环境管理工具不但能代替pip下载依赖还可以统一管理项目的依赖版本python版本和venv比起传统pipuv的优点是安装依赖很快多线程下载不但能管理依赖版本和下载依赖还能管理python版本和下载python本地缓存机制新项目不需再次下载相同的包根据配置对新clone的项目一键生成项目环境创建venv/安装依赖/激活python版本下载地址Releases · astral-sh/uv · GitHub解压将可执行文件目录放进环境变量即可。首先可以uv python list命令查看当前uv识别到已存在的python版本有些是其他途径下载安装进去的也能被识别到D:\PycharmProjectsuv python list cpython-3.15.0rc1-windows-x86_64-none download available cpython-3.15.0rc1freethreaded-windows-x86_64-none download available cpython-3.14.7-windows-x86_64-none download available cpython-3.14.7freethreaded-windows-x86_64-none download available cpython-3.14.6-windows-x86_64-none D:\miniconda3\python.exe cpython-3.13.15-windows-x86_64-none download available cpython-3.13.15freethreaded-windows-x86_64-none download available cpython-3.13.11-windows-x86_64-none D:\Python\Python313\python.exe cpython-3.12.14-windows-x86_64-none download available cpython-3.11.16-windows-x86_64-none C:\Users\lzj20\AppData\Roaming\uv\python\cpython-3.11.16-windows-x86_64-none\python.exe cpython-3.11.16-windows-x86_64-none C:\Users\lzj20\AppData\Roaming\uv\python\cpython-3.11-windows-x86_64-none\python.exe cpython-3.10.21-windows-x86_64-none download available cpython-3.9.25-windows-x86_64-none download available cpython-3.8.20-windows-x86_64-none download available pypy-3.11.15-windows-x86_64-none download available pypy-3.10.16-windows-x86_64-none download available pypy-3.9.19-windows-x86_64-none download available pypy-3.8.16-windows-x86_64-none download available graalpy-3.12.0-windows-x86_64-none download available graalpy-3.11.0-windows-x86_64-none download available graalpy-3.10.0-windows-x86_64-none download available没有需要的版本可以直接uv python install 版本号命令下载python例如uv python install 3.11接下来可以直接用uv创建一个my-project项目uv init --python 3.12 my-project会生成如下结构./my-project ├── .git ├── .gitignore ├── .python-version ├── pyproject.toml ├── README.md └── src/ └── my-project └── __init__.py其中.python-version里面写了个版本号用于锁死项目python版本pyproject.toml则是核心配置文件指定了项目名称版本python版本requires-python 3.12各种依赖模块和包的版本dependencies []这两个文件可共享到git.python-version3.12pyproject.toml[project] name my-project version 0.1.0 description Add your description here readme README.md authors [ { name liuzijian, email meliuzijian.com } ] requires-python 3.12 dependencies [] [project.scripts] my-project my_project:main [build-system] requires [uv_build0.12.5,0.13.0] build-backend uv_build执行uv sync命令会根据pyproject.toml配置一键生成项目环境生成venv/安装依赖/激活python版本依赖或python版本不存在的uv会自动下载D:\PycharmProjects\my-projectuv sync Using CPython 3.12.14 Creating virtual environment at: .venv执行后项目根目录下会生成虚拟环境目录.venv和用于统一下载地址的uv.lock文件接下来安装一个loguru依赖无需激活虚拟环境直接在根目录执行uv add loguru添加一个loguru模块到项目中uv会自动识别并添加依赖到虚拟环境完成后会发现依赖已经自动加入到pyproject.toml配置文件uv.lock文件里面保存了loguru的下载地址使用了uv add就尽量避免使用pip installuv add xxxversion可指定版本... ... dependencies [ loguru0.7.3, ] ... ...可以使用uv tree命令查看当前项目依赖树D:\PycharmProjects\my-projectuv tree Resolved 3 packages in 22ms my-project v0.1.0 └── loguru v0.7.3 ├── colorama v0.4.6 └── win32-setctime v1.2.0如要修改依赖或python版本也很简单先修改配置pyproject.toml然后再次执行uv sync命令如有python版本的改动.python-version文件中的python版本要和pyproject.toml同步修改。如卸载依赖可以采用uv remove loguru当使用PyCharm打开uv创建的项目时2026版的PyCharm能识别出并建议设置为uv项目如不能自动识别手动换为uv即可uv环境按实际情况设置解释器选择当前项目venv即可