知识点思维导图
29 个知识节点
参考资料
Python(07) - 面向对象
读完后,你应能完成以下任务:
- 绘制“Python(07) - 面向对象 / 边界一:self 不是 this,它必须"显式出现"”的关键对象与数据流,解释“每个实例方法的第一个参数必须是 self(名字是约定,理论上可改,但永远别改)。 -> 调用时 tom.greet() 会被 Python 自动翻译成 User.greet(tom),tom 被塞进 self。 -> 访问实例属性必须写 self.name,不能像 JS 那样在方法里直接写 name。”,并用源码位置、日志或 Trace 标注证据。
- 为“Python(07) - 面向对象 / 边界二:类属性 vs 实例属性(最隐蔽的坑)”设计正常与异常输入,验证“前端容易踩坑,是因为 JS 写在 class 体里的 count = 0 默认是实例字段,而 Python 写在 class 体里的是类属性——同样的位置,含义相反。”,输出首个偏差位置与回归测试结果。
- 实现“Python(07) - 面向对象 / 继承:和 extends 几乎一样,super 略有区别”的最小代码或配置,检验“Python super().init() 要自己手动调,否则父类的 init 不会跑(JS 里如果子类写了 constructor,不调 super 会直接报错,Python 不报错只是不执行,更隐蔽)。”,输出命令、结果与 Diff,并说明不适用边界。
你在前端写过
class extends、constructor、this。Python 的 OOP 八成长得像,但有几个"看着一样、其实不一样"的坑:self不是this、类属性会被所有实例共享、还有一堆__xxx__魔术方法。本篇帮你把 JS 的 class 心智模型平移过来,并划清差异。
一、先给锚点:和 ES6 class 几乎一一对应
你在 React/TS 里写过的 class,Python 里有等价物,连关键字都很像:
// JS / TS
class User {
constructor(name, age) {
this.name = name // 实例属性
this.age = age
}
greet() { // 实例方法
return `我是 ${this.name}`
}
}
const tom = new User("Tom", 18)
tom.greet()
对照表先建立直觉:
| JS / TS | Python | 说明 |
|---|---|---|
class User {} |
class User: |
关键字一致 |
constructor() |
def __init__(self) |
构造函数 |
this |
self |
当前实例(但 self 要显式写,见下) |
new User() |
User() |
Python 没有 new |
extends Base |
class Sub(Base) |
继承 |
super() |
super().__init__() |
调父类 |
static foo() |
@staticmethod |
静态方法 |
二、边界一:self 不是 this,它必须"显式出现"
这是前端最容易踩的第一个坑。JS 的 this 是隐式的、自动绑定的;Python 的 self 只是个普通参数,规则简单到有点反直觉:
- 每个实例方法的第一个参数必须是
self(名字是约定,理论上可改,但永远别改)。 - 调用时
tom.greet()会被 Python 自动翻译成User.greet(tom),tom被塞进self。 - 访问实例属性必须写
self.name,不能像 JS 那样在方法里直接写name。
// JS 对比:this 是隐式的,方法里直接写 this.count
class Counter {
constructor() { this.count = 0 }
add() { this.count += 1; return this.count } // 没有显式 this 参数
}
✅ 好消息:因为
self是显式传入的,Python 没有 JS 那种this丢失的问题。你不需要.bind(this)、不需要箭头函数来"锁定 this"。把方法当回调传出去,self也跟得牢牢的。
三、边界二:类属性 vs 实例属性(最隐蔽的坑)
JS 里你习惯所有状态都挂在 this 上。Python 多了一层"类属性"——直接写在 class 下、不在 __init__ 里的变量,它被所有实例共享。
读的时候没问题,但如果类属性是可变对象(list/dict),所有实例会共改一份数据——这是经典 bug:
正确写法:可变状态一律放进 __init__,让每个实例独立持有。
类比:类属性 ≈ JS 里写在 class 体里的
static字段(全类共享);实例属性 ≈ 写在constructor里挂到this上的字段(各实例独立)。前端容易踩坑,是因为 JS 写在 class 体里的count = 0默认是实例字段,而 Python 写在 class 体里的是类属性——同样的位置,含义相反。
四、继承:和 extends 几乎一样,super 略有区别
// JS 对比
class Cat extends Animal {
constructor(name, color) {
super(name) // 同样先调父类
this.color = color
}
speak() { return `${this.name} 喵喵叫` }
}
差异点:
- Python
super().__init__()要自己手动调,否则父类的__init__不会跑(JS 里如果子类写了 constructor,不调 super 会直接报错,Python 不报错只是不执行,更隐蔽)。 - Python 支持多继承
class C(A, B),JS 不支持。新手别急着用,理解单继承即可。
五、静态方法与类方法:对应 JS 的 static
JS 只有一种 static,Python 分两种,用装饰器区分(装饰器先当"标签"理解,第 10 篇细讲):
// JS 对比:只有 static 一种,静态和"工厂"都靠它
class User {
static isAdult(age) { return age >= 18 }
static fromString(text) {
const [name, age] = text.split(",")
return new User(name, Number(age))
}
}
User.isAdult(20)
记忆:@staticmethod ≈ JS 的纯 static(不碰类/实例);@classmethod 是 Python 特有,拿得到类本身 cls,最常见用途是提供"工厂方法"(如 User.from_json(data))。
六、魔术方法(dunder):定制对象的"内置行为"
带双下划线的方法 __xxx__(dunder = double underscore)由 Python 在特定时机自动调用,类似 JS 里 toString()、Symbol.iterator、valueOf() 这类"被引擎隐式调用的钩子"。
| 魔术方法 | 何时被调用 | JS 类比 |
|---|---|---|
__init__ |
User() 创建实例时 |
constructor |
__repr__ |
打印/调试时显示 | toString()(偏调试) |
__str__ |
str(obj) / print(obj) |
toString()(偏展示) |
__eq__ |
a == b 比较时 |
重写相等逻辑 |
__len__ |
len(obj) 时 |
.length getter |
__getitem__ |
obj[key] 取值时 |
索引/proxy get |
边界提醒:JS 不支持运算符重载(
a + b行为固定)。Python 的__add__这类能力比 JS 强,但别滥用——只在"语义天然"的地方用(金额、向量、坐标),否则代码会很迷惑。日常 80% 场景你只需要会写__init__和__repr__。
七、一个易混淆的小语法:没有 private,用下划线"约定"
Python 没有真正的 private。访问控制靠命名约定:
_balance:纯君子协定,等于 TS 里加了private但没编译检查——能访问,只是"你不该访问"。__pin:会被改名为_Account__pin,算是"防误碰",但不是安全机制。
新手记住一条:看到单下划线开头的属性/方法,就当它是内部的,别在外面调。
八、总结
- 边界一:self 不是 this,它必须"显式出现":每个实例方法的第一个参数必须是 self(名字是约定,理论上可改,但永远别改)。 -> 调用时 tom.greet() 会被 Python 自动翻译成 User.greet(tom),tom 被塞进 self。 -> 访问实例属性必须写 self.name,不能像 JS 那样在方法里直接写 name。
- 边界二:类属性 vs 实例属性(最隐蔽的坑):前端容易踩坑,是因为 JS 写在 class 体里的 count = 0 默认是实例字段,而 Python 写在 class 体里的是类属性——同样的位置,含义相反。
- 继承:和 extends 几乎一样,super 略有区别:Python super().init() 要自己手动调,否则父类的 init 不会跑(JS 里如果子类写了 constructor,不调 super 会直接报错,Python 不报错只是不执行,更隐蔽)。
- 静态方法与类方法:对应 JS 的 static:@classmethod 是 Python 特有,拿得到类本身 cls,最常见用途是提供"工厂方法"(如 User.from_json(data))。
- 魔术方法(dunder):定制对象的"内置行为":边界提醒:JS 不支持运算符重载(a + b 行为固定)。
- 一个易混淆的小语法:没有 private,用下划线"约定":_balance:纯君子协定,等于 TS 里加了 private 但没编译检查——能访问,只是"你不该访问"。
学完自测
选择所有正确答案;提交后逐项核对判断依据。