📌 一句话提纲
Python 的 OOP 从 class 出发,覆盖构造函数、属性管理、方法类型、继承体系与 MRO,以及双下划线魔法方法。
1. 类与实例
基本定义
python
class Dog:
# 类属性(所有实例共享)
species = "Canis familiaris"
# 构造函数
def __init__(self, name: str, age: int):
self.name = name # 实例属性
self.age = age
# 实例方法
def bark(self):
return f"{self.name} says Woof!"
python
d1 = Dog("Buddy", 3)
d2 = Dog("Max", 5)
print(d1.name) # Buddy
print(d1.species) # Canis familiaris(类属性)
print(d2.species) # Canis familiaris
Dog.species = "新物种" # 修改类属性影响所有实例
print(d1.species) # 新物种
实例属性 vs 类属性陷阱
python
class Cat:
tricks = []
def __init__(self, name):
self.name = name
def learn(self, trick):
self.tricks.append(trick) # 坑!修改的是类属性,所有实例共享
c1 = Cat("Kitty")
c2 = Cat("Mimi")
c1.learn("roll over")
print(c2.tricks) # ['roll over'] 😱
# 正确做法
class Cat:
def __init__(self, name):
self.name = name
self.tricks = [] # 实例属性,每个猫独立
def learn(self, trick):
self.tricks.append(trick)
避坑: 不要在方法里修改可变类属性(list/dict),要用实例属性。
2. 三种方法类型
python
class MyClass:
class_var = "类变量"
def __init__(self, value):
self.value = value
# ① 实例方法 ------ 第一个参数 self
def instance_method(self):
return f"实例方法: {self.value}"
# ② 类方法 ------ 第一个参数 cls
@classmethod
def class_method(cls):
return f"类方法: {cls.class_var}"
# ③ 静态方法 ------ 没有 self/cls
@staticmethod
def static_method(x, y):
return x + y
python
obj = MyClass(42)
print(obj.instance_method()) # 实例方法: 42
print(MyClass.class_method()) # 类方法: 类变量
print(MyClass.static_method(3, 5)) # 8
| 类型 | 装饰器 | 访问实例 | 访问类 | 适用场景 |
|---|---|---|---|---|
| 实例方法 | 无 | ✅ | ✅ | 操作实例状态 |
| 类方法 | @classmethod |
❌ | ✅ | 工厂方法、替代构造器 |
| 静态方法 | @staticmethod |
❌ | ❌ | 工具函数,本质上只是放在类命名空间 |
@classmethod 典型场景:工厂方法
python
from datetime import date
class Person:
def __init__(self, name: str, birth_year: int):
self.name = name
self.birth_year = birth_year
self.age = date.today().year - birth_year
@classmethod
def from_birthday(cls, name: str, birthday: str):
"""从生日字符串创建实例(替代构造器)"""
year = int(birthday.split("-")[0])
return cls(name, year)
p = Person.from_birthday("Alice", "1995-06-15")
print(f"{p.name}, {p.age}岁") # Alice, 31岁
3. 继承与 super()
python
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
raise NotImplementedError("子类必须实现")
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name) # 调用父类构造
self.color = color
def speak(self):
return f"{self.name} says 喵~"
class Dog(Animal):
def speak(self):
return f"{self.name} says 汪!"
python
animals = [Cat("小花", "橘色"), Dog("旺财")]
for a in animals:
print(a.speak())
# 小花 says 喵~
# 旺财 says 汪!
核心规则:
- 永远用
super().__init__()调用父类构造,不要写Parent.__init__(self) - 多继承时
super()保证 MRO 顺序正确
4. MRO(方法解析顺序)
python
class A:
def method(self): return "A"
class B(A):
def method(self): return "B"
class C(A):
def method(self): return "C"
class D(B, C):
pass
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
print(D().method()) # B(C3 线性化)
MRO 采用 C3 线性化:广度优先 + 保证单调性。
super() 不是 "父类" 而是 "MRO 中的下一个",多继承时极其有用。
5. 私有属性(Name Mangling)
python
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self._internal_id = 123 # 约定"私有",实际上可以访问
self.__password = "secret" # Name Mangling → _BankAccount__password
def get_balance(self):
return self.__password
acc = BankAccount("Alice", 1000)
print(acc._internal_id) # 123(能访问但不推荐)
# print(acc.__password) # ❌ AttributeError
print(acc._BankAccount__password) # "secret"(知道规则就能访问)
| 命名 | 含义 | 实际行为 |
|---|---|---|
name |
公开 | 正常访问 |
_name |
保护(约定) | 正常访问,但 IDE 会提示 |
__name |
私有(Name Mangling) | 变为 _ClassName__name |
__name__ |
魔法方法 | 保留给 Python 特殊方法 |
6. @property(属性装饰器)
替代 getter/setter 的 Python 方式
python
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("温度不能低于绝对零度")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
t = Temperature(25)
print(t.celsius) # 25(像访问属性一样)
t.celsius = 30 # 走 setter
print(t.fahrenheit) # 86.0(计算属性)
# t.celsius = -300 # ❌ ValueError
优点: 外部调用方式不变,内部可以加校验/计算逻辑。
7. 常用特殊方法
python
class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
def __str__(self):
return f"《{self.title}》- {self.author}"
def __repr__(self):
return f"Book('{self.title}', '{self.author}', {self.pages})"
def __len__(self):
return self.pages
def __eq__(self, other):
if not isinstance(other, Book):
return NotImplemented
return self.title == other.title and self.author == other.author
def __lt__(self, other):
return self.pages < other.pages
book1 = Book("Python实战", "张三", 300)
book2 = Book("Python实战", "张三", 300)
print(str(book1)) # 《Python实战》- 张三
print(repr(book1)) # Book('Python实战', '张三', 300)
print(len(book1)) # 300
print(book1 == book2) # True
print(book1 < Book("数据结构", "李四", 500)) # True
📝 要点总结
| 概念 | 要点 |
|---|---|
| 类属性 vs 实例属性 | 可变对象务必放 __init__ 里 |
@classmethod |
工厂方法/替代构造器的最佳选择 |
super() |
始终用它调用父类构造,支持多继承 |
| MRO | C3 线性化,查看用 ClassName.__mro__ |
__ 私有属性 |
只是 Name Mangling,不是真正的 private |
@property |
Python 风格的 getter/setter,优雅又安全 |
| 特殊方法 | __str__ 给人看,__repr__ 给开发者看 |