前言
python 是一种以 “更好写、更好读、适合日常工程工作 ”被开发出来的语言。它目标不是“性能最强”,而是:
- 语法清晰,代码像人能读懂的话
- 写得快,改得快
- 适合脚本、工具、系统管理、原型开发
- 既简单,又不是玩具
- 能和 C 等底层语言配合
这也是为什么 Python 后来会特别适合:
- 自动化脚本
- 运维工具
- Web 开发
- 数据分析
- AI / 机器学习
因为它的出发点本来就是“让人高效完成工作”。
Python的发展历史
Python 从发展至今,已经有三个大版本的更新了,出于其目的可归结为
- Python 1 回答的是:程序为什么非得这么难写?
- Python 2 回答的是:这门语言能不能真的扛工程?
- Python 3 回答的是:已经成功的语言,怎么修掉历史债务继续往前走?
Python 1:解决“写程序太费劲、太难读”的问题
Python 1 的思路非常明确:让语言优先服务“人写代码”的体验。它主要通过这些设计解决问题:
- 用缩进表达代码块
- 强制代码结构清晰
- 减少花括号、begin/end 之类噪音
- 语法尽量简单直白
- 让代码更接近自然阅读
- 内置高层数据结构
- list、dict、字符串处理这些开箱可用
- 不需要开发者自己从底层拼很多东西
- 模块化机制
- 代码可以拆文件、拆模块,不必全堆在一起
- 异常机制
- 错误处理比单纯返回错误码更清晰
- 解释执行
- 试验、修改、迭代很快
所以 Python 1 解决的核心,不是“性能问题”,而是: 把“写程序”从偏底层、偏啰嗦,拉到偏表达、偏效率。
Python 2:解决“好写还不够,还得能做大项目”的问题
Python 2 不是推翻 Python 1,而是在它基础上把“工程能力”补齐。它主要通过这些方向解决问题:
- 增强语言表达力
- 列表推导式、生成器、迭代器、装饰器等能力逐渐成熟
- 让开发者能用更简洁、更抽象的方式写逻辑
- 完善对象模型和标准库
- 让 Web、文件、网络、文本、测试等常见需求更容易落地
- 提升大型程序可组织性
- 模块、包、类、异常体系更完整
- 开始认真处理 Unicode
- 虽然最后没彻底解决,但 Python 2 时代已经意识到国际化文本是核心问题
- 形成生态
- 这时 Python 才真正大规模进入生产环境
所以 Python 2 解决的核心是:让 Python 从“写脚本很舒服”,升级成“做真实业务也很能打”。 但 Python 2 也留下了一个重大副作用:它为了兼容历史,慢慢积累了不少设计包袱,尤其是字符串/编码、标准库一致性这些问题。
Python 3:解决“历史包袱太重,继续修补会越来越乱”的问题
问题
Python 2 很成功,但成功之后,老设计的问题被无限放大了。最核心的几个问题是:
- 文本和字节混乱
- 语言规则不够一致
- 历史兼容成本太高
Python 3 的解决方式很硬:宁可不兼容,也要把基础规则理顺。 主要是这样做的:
- 彻底区分文本和二进制
- str 表示文本
- bytes 表示字节
- 不再允许模糊混用
- 这让编码、I/O、网络、文件处理逻辑更清晰
- 统一语义
- print 改成函数
- / 改成真正除法,// 表示整除
- range 统一成更现代的行为
- map / filter / zip 更偏迭代器风格
- 清理标准库
- 调整一些模块命名和组织方式
- 让整体结构更统一
- 统一类模型
- 去掉 Python 2 里旧式类 / 新式类的割裂
- 为未来工具链和大型工程打基础
- 更适合类型注解、异步编程、现代 packaging、跨平台文本处理
所以 Python 3 解决的核心是:把 Python 从“历史上很好用但内部不够整洁的语言”,变成“可以长期演进的现代语言”。
Python环境
一个普通 Python 应用怎么落地
- 用 uv / pyenv/ conda 选 Python版本建环境
- 用 venv 得到 .venv
- 用 pip 安装依赖
- 程序运行:python main.py
- 如果要把项目做成可安装包,再用 setuptools /build 构建, 产出 wheel 包文件
Python3的项目结构
Python 3 最小常规工程的项目结构
py-test/ # 项目根目录
├── pyproject.toml # 项目配置,定义依赖、Python 版本、构建方式
├── README.md # 项目说明文档
├── src/ # 源码根目录
│ └── py_test/ # 主 Python 包目录
│ ├── __init__.py # 包初始化文件
│ └── main.py # 项目主入口或核心启动文件
└── tests/ # 测试目录
└── test_main.py # 主入口或核心逻辑的基础测试
pyproject配置
pyproject.toml 拆成三种职责:
- 构建系统声明
- 项目元信息
- 工具配置(为1声明的构建工具提供配置)
## 如果没有这一段,pip install -e .、构建 wheel、editable install 这些行为就没有标准入口。
[build-system]
## 告诉安装工具,构建项目之前先准备 setuptools 和 wheel
requires = ["setuptools>=68", "wheel"]
## 告诉工具,真正负责把项目打包成可安装形式的是 setuptools.build_meta
build-backend = "setuptools.build_meta"
[project]
## 提供了项目元信, 定义这个项目“是谁”
name = "py-test"
version = "0.1.0"
description = "Reference Python 3 project structure with CLI, API, services, and tests"
readme = "README.md"
requires-python = ">=3.11"
authors = [{ name = "OpenAI Example" }]
license = { text = "MIT" }
## 定义这个项目“运行需要什么依赖”
dependencies = [
"fastapi>=0.115,<1",
"pydantic>=2.8,<3",
"pydantic-settings>=2.4,<3",
"uvicorn>=0.30,<1",
]
## 你自己定义的“可选依赖分组”
[project.optional-dependencies]
## 开发者可以通过 pip install -e ".[dev]" 一次性装齐开发环境
dev = [
"httpx>=0.27,<1",
"mypy>=1.11,<2",
"pytest>=8,<9",
"pytest-cov>=5,<6",
"ruff>=0.6,<1",
]
## 测试环境
test =[
"py-test[dev]" ## 可以这么引用dev依赖
]
## 定义这个项目“安装后暴露什么命令”
[project.scripts]
# 生成一个命令:py-test
# 这个命令的目标函数是:
# - 模块:py_test.cli
# - 函数:main
# 原理: 也是 shebang
py-test = "py_test.cli:main"
## 源码包地址,给构建工具
[tool.setuptools.packages.find]
where = ["src"]
# 指定测试目录和默认参数
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"
# 指定 Python 目标版本和 lint 规则
[tool.ruff]
target-version = "py311"
line-length = 100
[tool.ruff.lint]
select = ["B", "E", "F", "I", "UP"]
# 指定类型检查版本和检查范围
[tool.mypy]
python_version = "3.11"
strict = true
packages = ["py_test"]
pyproject.toml 是“现代做法”,但很多用户的第一反应仍然是:
pip install -r requirements.txt
其意思是让 pip install 去读取 requirements.txt 这个文件,把文件里的每一行都当成安装要求来处理
## requirements.txt
## 文件内容的核心是:
-e .[dev]
python模块
Python 模块命名空间的形式,采用"点模块名称"。
比如一个模块的名称是 A.B, 那么他表示一个包 A中的子模块 B 。
在导入一个包的时候,Python 会根据 sys.path 中的目录来寻找这个包中包含的子目录。
目录只有包含一个叫做 init.py 的文件才会被认作是一个包
sound/ 顶层包
__init__.py 初始化 sound 包
effects/ 声音效果子包
__init__.py
echo.py
surround.py
reverse.py
在运行文件中可以使用以下方式导入
## 导包
import sound.effects.echo
# 这将会导入子模块:sound.effects.echo。 他必须使用全名去访问:
sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
## 导入子模块的方法
from sound.effects import echo
## echo,并且他不需要那些冗长的前缀,所以他可以这样使用:
echo.echofilter(input, output, delay=0.7, atten=4)
## 直接导入一个函数或者变量:
from sound.effects.echo import echofilter
## 可以直接使用他的 echofilter() 函数:
echofilter(input, output, delay=0.7, atten=4)
## 找到这个包里面所有的子模块,然后一个一个的把它们都导入进来
from sound.effects import *
## 如果包定义文件 __init__.py 存在一个叫做 __all__ 的列表变量,那么在使用 from package import * 的时候就把这个列表中的所有名字作为包内容导入。
## sounds/effects/__init__.py 中包含如下代码:
__all__ = ["echo", "surround", "reverse"]
按模块/包运行
Python 包可以通过 包名 的方式直接运
python -m py_test
当你用这方式时,Python 会去找:
py_test/__main__.py
- 如果找到了,就执行它。
- 如果没有,通常就不能这样把整个包当可运行入口来启动。
这对开发阶段很方便,也更符合 Python 原生方式。
python的执行过程
Python 执行一个 .py 文件,核心流程可以概括成:
源码 .py -> 编译成字节码 -> 交给 Python 虚拟机执行 -> 可选地缓存成 .pyc
- 第一步:读取源码文件
Python 解释器首先会做的不是直接“逐字符执行”,而是先把这个文件读进来。 - 第二步:词法分析和语法分析
Python 读到源码后,会先做解析工作,大致包括:- 把字符流切成 token
- 按语法规则分析结构
- 检查语法是否正确
- 第三步:编译成字节码
这里的“编译”不是指像 C 那样编译成机器码,而是编译成一种给 Python 虚拟机执行的中间指令。 - 第四步:交给 Python 虚拟机执行
编译完字节码后,Python 并不会把它变成原生二进制,而是交给 Python 虚拟机 去执行。 - 第五步:把字节码缓存成 .pyc ,
Python 通常会把编译结果缓存下来,写入:pycache/ 里面的 .pyc 文件
这样下次再次导入同一个模块时,如果源码没变化,就可以直接使用缓存,不必重新编译。也可以使用以前命令提前编译python -m compileall -q src
从上面流程来看与JAVA的流程相似,
- 共同点是:都不是直接执行源码,而是先转成字节码,再交给自己的虚拟机执行。
- 不同是:Java 把字节码当正式产物来管理,Python 通常把字节码当内部缓存来使用。
为什么 Python 明明也有“编译”,但我们仍然把它叫解释型语言?
“解释型 / 编译型”这个分类,本来就不是看“有没有编译”,而是看程序最终是怎么被执行、编译阶段对用户是什么形态。 Python 之所以被叫解释型语言,不是因为它完全没有编译,而是因为它不会先生成独立机器码产物再运行,而是通常由解释器在运行时将源码编译为字节码并交给虚拟机执行。
Python虚拟机
| 维度 | Python虚拟机(CPython) | JVM(Java HotSpot) | 关键区别 |
|---|---|---|---|
| 本体是什么 | Python 官方实现里的运行时核心 | Java 官方主流运行时 | CPython 是“语言实现的一部分”,JVM 是“语言平台核心” |
| 输入源码 | .py | .java | 都先从源码开始 |
| 中间产物 | Python 字节码,常见缓存为 .pyc | JVM 字节码,文件是 .class | 两边都有字节码 |
| 字节码标准化程度 | 偏实现内部细节,主要跟着 CPython 版本走 | 高度标准化,JVM 规范明确 | JVM 的字节码规范更稳定、更统一 |
| 执行核心 | 字节码解释器逐条执行 | 解释执行 + JIT 编译 | JVM 会把热点代码编译成机器码,CPython 默认不会 |
| JIT | 默认没有成熟生产级 JIT | 有成熟 HotSpot JIT(C1/C2) | 这是性能差异的核心来源之一 |
| 热点优化 | 很有限 | 很强,支持内联、逃逸分析、锁消除等 | JVM 更擅长长期运行后的性能爬升 |
| 内存管理 | 引用计数 + 循环GC | Tracing GC,分代回收、多种收集器 | CPython 更简单直接,JVM 更复杂但更强 |
| 对象管理成本 | 动态对象开销大,属性查找和调用成本高 | 类型信息更稳定,更利于优化 | Python 的动态性更灵活,也更贵 |
| 类型系统 | 动态类型 | 静态类型 | JVM 更容易做激进优化 |
| 函数/方法调用 | 运行时动态分派更多 | 调用路径更可预测 | Java 方法调用更容易被 JIT 内联 |
| 线程模型 | 有原生线程,但有 GIL | 原生线程,无 GIL | CPU 密集多线程时,Java 更容易吃满多核 |
| 多核并行 | 多进程更常见 | 多线程并行更自然 | Python 想并行常靠进程池或底层库 |
| 启动速度 | 通常较快 | 通常较慢一点 | JVM 需要类加载、JIT 预热等 |
| 预热特性 | 基本无“越跑越快”这一套 | 明显有 warm-up,越跑越容易快 | 长跑服务通常 JVM 更占优 |
| 纯CPU计算 | 通常较慢 | 通常较快 | 同样算法,Java 往往快很多 |
| I/O 场景 | 表现可以很好 | 也很好 | I/O 场景差距没纯计算那么大 |
| 本地扩展 | 大量依赖 C/C++ 扩展提速 | JNI 也能调本地库 | Python 常把重活下沉给 numpy、pandas、torch |
| 打包后运行 | 常见是源码 + 环境运行 | 常见是 jar 跑在 JVM 上 | Python 更偏脚本/环境驱动,Java 更偏平台/制品驱动 |
| 兼容性边界 | 很多库默认优先支持 CPython | Java 生态天然围绕 JVM | Python 生态“解释器差异”更值得关注 |
| 典型代表命令 | python app.py | java -jar app.jar | 使用方式也反映平台差异 |
语法笔记
记录属于python特有的语法笔记内容

5728

被折叠的 条评论
为什么被折叠?



