PHP → Python 迁移路线

60 天 Python 学习计划 · 从 Laravel 到 Django

基于 jackfrued/Python-100-Days 官方大纲压缩重排。你已有的编程基础不做重复投资,时间全部压在 PHP 没有的那部分:Python 数据模型、Django 全家桶、数据分析、工程化部署。

60 天 · 9 阶段 日均 1–2 小时 60 项可勾选任务 全程 PHP 对照 源:Python-100-Days
0%0 / 60 天
⌂ 学习站 登录同步

全程路线图

条块宽度 = 该阶段占用的天数
Day 1Day 20Day 40Day 60

能力分层图

从语法到工程化的依赖关系
第 1 层 · 语言能力 第 2 层 · 应用能力 第 3 层 · 工程实战 语法 · 类型 · 流程 list/dict/tuple/set 函数 · 装饰器 OOP · 属性协议 迭代器 · 生成器 · 并发 文件 · 异常 · JSON Excel/Word/PDF MySQL · 索引 DB-API · 事务 正则 · 请求网络 Django · ORM · DRF Celery · Redis 缓存 爬虫 · Scrapy NumPy · pandas 部署上线

60 天任务排期

点阶段标题展开 / 收起 · 点方框打勾(自动保存)

PHP → Python 语法速查表

前两周贴在手边
主题PHPPython关键差异
语句结束 / 代码块$x = 1; { }x = 1 换行 + 缩进无分号;缩进就是语法,4 空格统一,混用 Tab 直接报错
变量$name = 'x';name = "x"没有 $ 前缀;命名用 snake_case(PHP 是 camelCase/PascalCase)
字符串拼接$a . $bf"{a}{b}"Python 不用 .(那是方法调用);f-string 是主力,性能也最好
相等比较== 松散 / === 严格== 值 / is 身份Python 的 == 默认就是「严格」语义,is 判同一对象(None 只能用 is)
逻辑运算&& \|\| !and or not短路行为一致,但 Python 返回的是操作数本身,不是 bool
整除 / 幂intdiv() **// **Python 有原生整除运算符;/ 永远返回 float
三元表达式$c ? $a : $ba if c else b语序不同,习惯后反而更顺
条件分支elseif / switchelif / match(3.10+)Python 的 match 是模式匹配,能力远超 switch
循环for($i=0;...) foreachfor 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, **kwargsPython 区分位置参数包和关键字参数包,是框架回调的通用写法
匿名函数fn($x) => ...lambda x: ...Python 的 lambda 只能是单个表达式,不能写语句块
对象属性$this->nameself.nameself 必须作为第一个参数显式声明,函数即方法
访问控制public/private/protected_x / __xPython 只有约定,没有真私有;__x 只是名字改写(name mangling)
构造函数__construct()__init__(self, ...)不能重载;多态靠 *args 或工厂函数实现
Getter / Setter__get() __set()@propertyPython 用装饰器把方法伪装成属性,调用方写 obj.name 而不是 obj.getName()
继承单继承 + trait多继承 + MROPython 支持多继承,方法解析顺序按 C3 线性化,注意 super() 的走法
JSONjson_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 会报错
ORMEloquent / ThinkPHP ModelDjango ORM / SQLAlchemy思路高度相似:模型 → 迁移 → 查询构造器。Django 的 QuerySet 是惰性求值的
迁移php artisan migratepython manage.py migrate概念几乎一一对应,Django 用 makemigrations 先生成迁移文件
模板Blade {{ $x }}DTL {{ x }}语法极像;Django 模板不能写复杂表达式,逻辑要留在视图层
中间件Laravel MiddlewareDjango Middleware都是洋葱模型;Django 的请求/响应钩子在同一个类里成对出现
队列Laravel Queue / HorizonCelery + Redis/RabbitMQCelery 是独立进程 + 独立配置,比 Laravel Queue 重,但生态更成熟
依赖管理composer.json + vendor/pyproject.toml + venv(推荐用 uv)最大工程习惯差异:Python 每个项目必须有独立虚拟环境

PHP 选手最容易踩的 6 个坑

提前打预防针

1. 把 list 当 array 用

  • PHP 里 $a[] = 1 和 $a['k'] = 1 是同一个东西,Python 里是两个物种。
  • 往 list 里塞键值对会直接报错。需要 map 就用 dict。
  • tuple 不可变,适合当「记录」;set 自动去重,适合做存在性判断和白名单。

2. 可变默认参数

  • def f(items=[]): —— 这个 [] 在函数定义时创建一次,所有调用共享同一个列表。
  • PHP 的默认参数每次调用都是新值,所以这个坑特别容易中招。
  • 正解:def f(items=None): items = items or []。

3. 缩进混用 Tab 和空格

  • PHP 里缩进纯属审美,Python 里缩进是语法。
  • 同一个文件混用 Tab 和空格会抛 TabError。
  • 编辑器统一设成 4 空格,并在保存时自动转换。

4. 忘了开虚拟环境

  • PHP 是「全局 PHP + 项目级 composer」,Python 是「项目级 Python + 项目级依赖」。
  • 直接 pip install 到全局,迟早会遇到版本冲突地狱。
  • 习惯:每个项目 uv venv / python -m venv .venv,然后 source .venv/bin/activate。

5. PyMySQL 占位符写成 ?

  • PDO 的习惯是 WHERE id = ?,PyMySQL 要写 WHERE id = %s。
  • 写 %d 也一样会报错 —— 数字也用 %s。
  • SQLAlchemy 里则用 :name 命名参数,又是另一套,别混。

6. 用 == 比较 None

  • PHP 里 == null 很自然,Python 里应该写 is None。
  • 因为 None 是单例,is 才是语义正确且更快的判断。
  • 同理:判空用 if not x:,但要注意 0、""、[] 都会被判为假。