前言

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 很成功,但成功之后,老设计的问题被无限放大了。最核心的几个问题是:

  1. 文本和字节混乱
  2. 语言规则不够一致
  3. 历史兼容成本太高

Python 3 的解决方式很硬:宁可不兼容,也要把基础规则理顺。 主要是这样做的:

  • 彻底区分文本和二进制
    • str 表示文本
    • bytes 表示字节
    • 不再允许模糊混用
    • 这让编码、I/O、网络、文件处理逻辑更清晰
  • 统一语义
    • print 改成函数
    • / 改成真正除法,// 表示整除
    • range 统一成更现代的行为
    • map / filter / zip 更偏迭代器风格
  • 清理标准库
    • 调整一些模块命名和组织方式
    • 让整体结构更统一
  • 统一类模型
    • 去掉 Python 2 里旧式类 / 新式类的割裂
  • 为未来工具链和大型工程打基础
    • 更适合类型注解、异步编程、现代 packaging、跨平台文本处理

所以 Python 3 解决的核心是:把 Python 从“历史上很好用但内部不够整洁的语言”,变成“可以长期演进的现代语言”。

Python环境

一个普通 Python 应用怎么落地

  1. 用 uv / pyenv/ conda 选 Python版本建环境
  2. 用 venv 得到 .venv
  3. 用 pip 安装依赖
  4. 程序运行:python main.py
  5. 如果要把项目做成可安装包,再用 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. 构建系统声明
  2. 项目元信息
  3. 工具配置(为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

  1. 第一步:读取源码文件
    Python 解释器首先会做的不是直接“逐字符执行”,而是先把这个文件读进来。
  2. 第二步:词法分析和语法分析
    Python 读到源码后,会先做解析工作,大致包括:
    • 把字符流切成 token
    • 按语法规则分析结构
    • 检查语法是否正确
  3. 第三步:编译成字节码
    这里的“编译”不是指像 C 那样编译成机器码,而是编译成一种给 Python 虚拟机执行的中间指令。
  4. 第四步:交给 Python 虚拟机执行
    编译完字节码后,Python 并不会把它变成原生二进制,而是交给 Python 虚拟机 去执行。
  5. 第五步:把字节码缓存成 .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特有的语法笔记内容

更多推荐