基于 jackfrued/Python-100-Days 官方大纲压缩重排。你已有的编程基础不做重复投资,时间全部压在 PHP 没有的那部分:Python 数据模型、Django 全家桶、数据分析、工程化部署。
| 主题 | PHP | Python | 关键差异 |
|---|---|---|---|
| 语句结束 / 代码块 | $x = 1; { } | x = 1 换行 + 缩进 | 无分号;缩进就是语法,4 空格统一,混用 Tab 直接报错 |
| 变量 | $name = 'x'; | name = "x" | 没有 $ 前缀;命名用 snake_case(PHP 是 camelCase/PascalCase) |
| 字符串拼接 | $a . $b | f"{a}{b}" | Python 不用 .(那是方法调用);f-string 是主力,性能也最好 |
| 相等比较 | == 松散 / === 严格 | == 值 / is 身份 | Python 的 == 默认就是「严格」语义,is 判同一对象(None 只能用 is) |
| 逻辑运算 | && \|\| ! | and or not | 短路行为一致,但 Python 返回的是操作数本身,不是 bool |
| 整除 / 幂 | intdiv() ** | // ** | Python 有原生整除运算符;/ 永远返回 float |
| 三元表达式 | $c ? $a : $b | a if c else b | 语序不同,习惯后反而更顺 |
| 条件分支 | elseif / switch | elif / match(3.10+) | Python 的 match 是模式匹配,能力远超 switch |
| 循环 | for($i=0;...) foreach | for i in range(n) | Python 只有 for-in,没有 C 风格三段式;配 enumerate() 拿下标 |
| 数组 | 一个 array 通吃索引数组和关联数组 | list / dict / tuple / set | 最需要改的心智模型:Python 严格区分四种结构,各有适用场景 |
| 关联数组 | ['k' => 'v'] | {"k": "v"} | Python 的 dict 保持插入顺序;取值用 d.get(k, default) 而不是 ?? |
| 函数定义 | function f($a = 1){} | def f(a=1): | 无返回类型强制(可用 type hints + mypy);参数默认值只在定义时求值一次(坑) |
| 可变参数 | ...$args | *args, **kwargs | Python 区分位置参数包和关键字参数包,是框架回调的通用写法 |
| 匿名函数 | fn($x) => ... | lambda x: ... | Python 的 lambda 只能是单个表达式,不能写语句块 |
| 对象属性 | $this->name | self.name | self 必须作为第一个参数显式声明,函数即方法 |
| 访问控制 | public/private/protected | _x / __x | Python 只有约定,没有真私有;__x 只是名字改写(name mangling) |
| 构造函数 | __construct() | __init__(self, ...) | 不能重载;多态靠 *args 或工厂函数实现 |
| Getter / Setter | __get() __set() | @property | Python 用装饰器把方法伪装成属性,调用方写 obj.name 而不是 obj.getName() |
| 继承 | 单继承 + trait | 多继承 + MRO | Python 支持多继承,方法解析顺序按 C3 线性化,注意 super() 的走法 |
| JSON | json_encode() / json_decode() | json.dumps() / json.loads() | 命名反直觉:dumps = 编码成字符串,loads = 解析(s = string) |
| 读取文件 | file_get_contents() | with open(p, encoding="utf-8") as f: | Python 强烈推荐 with 上下文管理器,等价于 PHP 的 RAII 风格自动关闭 |
| 数据库 | PDO / mysqli,占位符 ? | PyMySQL / SQLAlchemy,占位符 %s | 常见坑:PyMySQL 用 %s 且即使是数字也用 %s;%d 会报错 |
| ORM | Eloquent / ThinkPHP Model | Django ORM / SQLAlchemy | 思路高度相似:模型 → 迁移 → 查询构造器。Django 的 QuerySet 是惰性求值的 |
| 迁移 | php artisan migrate | python manage.py migrate | 概念几乎一一对应,Django 用 makemigrations 先生成迁移文件 |
| 模板 | Blade {{ $x }} | DTL {{ x }} | 语法极像;Django 模板不能写复杂表达式,逻辑要留在视图层 |
| 中间件 | Laravel Middleware | Django Middleware | 都是洋葱模型;Django 的请求/响应钩子在同一个类里成对出现 |
| 队列 | Laravel Queue / Horizon | Celery + Redis/RabbitMQ | Celery 是独立进程 + 独立配置,比 Laravel Queue 重,但生态更成熟 |
| 依赖管理 | composer.json + vendor/ | pyproject.toml + venv(推荐用 uv) | 最大工程习惯差异:Python 每个项目必须有独立虚拟环境 |
$a[] = 1 和 $a['k'] = 1 是同一个东西,Python 里是两个物种。list 里塞键值对会直接报错。需要 map 就用 dict。def f(items=[]): —— 这个 [] 在函数定义时创建一次,所有调用共享同一个列表。def f(items=None): items = items or []。TabError。pip install 到全局,迟早会遇到版本冲突地狱。uv venv / python -m venv .venv,然后 source .venv/bin/activate。?WHERE id = ?,PyMySQL 要写 WHERE id = %s。%d 也一样会报错 —— 数字也用 %s。:name 命名参数,又是另一套,别混。== 比较 None== null 很自然,Python 里应该写 is None。None 是单例,is 才是语义正确且更快的判断。if not x:,但要注意 0、""、[] 都会被判为假。