Python 面向对象:init 之外你需要知道的
Python OOP 不只是 class + __init__,魔术方法、描述符、元类才是真正的武器。这篇从基础到进阶,系统梳理 Python OOP 的全貌。
类的基础:五个必知魔术方法
class Vector:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __repr__(self) -> str:
# 开发者用,调试时显示,eval() 可还原
return f"Vector({self.x!r}, {self.y!r})"
def __str__(self) -> str:
# 用户友好,print() 时调用;未定义则退化到 __repr__
return f"({self.x}, {self.y})"
def __eq__(self, other) -> bool:
# 定义 == 的行为
if not isinstance(other, Vector):
return NotImplemented
return self.x == other.x and self.y == other.y
def __hash__(self) -> int:
# 定义 hash(),使对象可作 dict key/set 元素
# 注意:定义了 __eq__ 后必须显式定义 __hash__,否则不可哈希
return hash((self.x, self.y))
v1 = Vector(1, 2)
v2 = Vector(1, 2)
print(repr(v1)) # Vector(1, 2)
print(str(v1)) # (1, 2)
print(v1 == v2) # True
d = {v1: "origin"} # 可以,因为有 __hash__
继承与 MRO
单继承
class Animal:
def __init__(self, name: str):
self.name = name
def speak(self) -> str:
raise NotImplementedError
class Dog(Animal):
def speak(self) -> str:
return f"{self.name} says Woof!"
class Cat(Animal):
def speak(self) -> str:
return f"{self.name} says Meow!"
多继承与 MRO(C3 线性化)
class A:
def greet(self): return "A"
class B(A):
def greet(self): return "B -> " + super().greet()
class C(A):
def greet(self): return "C -> " + super().greet()
class D(B, C):
def greet(self): return "D -> " + super().greet()
print(D().greet()) # D -> B -> C -> A
print(D.__mro__)
# (<class D>, <class B>, <class C>, <class A>, <class object>)
MRO 规则:C3 算法保证:
- 子类在父类前面
- 多个父类按声明顺序排列
- 单调性(父类的相对顺序保持不变)
super() 不是"调用父类",是"按 MRO 顺序调用下一个类",这在多继承中至关重要。
三种方法类型
class Circle:
PI = 3.14159
def __init__(self, radius: float):
self.radius = radius
# 实例方法:第一个参数是 self(实例)
def area(self) -> float:
return self.PI * self.radius ** 2
# 类方法:第一个参数是 cls(类本身),可通过类或实例调用
@classmethod
def from_diameter(cls, diameter: float) -> "Circle":
return cls(diameter / 2)
# 静态方法:没有 self 或 cls,只是逻辑上属于这个类的函数
@staticmethod
def is_valid_radius(r: float) -> bool:
return r > 0
c1 = Circle(5)
c2 = Circle.from_diameter(10) # 类方法
Circle.is_valid_radius(-1) # False
@property:受控属性访问
class Temperature:
def __init__(self, celsius: float = 0):
self._celsius = celsius # 约定:_ 前缀表示"内部使用"
@property
def celsius(self) -> float:
return self._celsius
@celsius.setter
def celsius(self, value: float):
if value < -273.15:
raise ValueError(f"Temperature {value} below absolute zero!")
self._celsius = value
@celsius.deleter
def celsius(self):
print("Deleting temperature")
del self._celsius
@property
def fahrenheit(self) -> float:
# 只读属性(没有 setter)
return self._celsius * 9/5 + 32
t = Temperature(25)
print(t.celsius) # 25
print(t.fahrenheit) # 77.0
t.celsius = 100 # 调用 setter
# t.celsius = -300 # ValueError!
del t.celsius # 调用 deleter
slots:内存优化
默认情况下,Python 对象把实例属性存在 __dict__(字典)中,灵活但占内存。
import sys
class PointDict:
def __init__(self, x, y):
self.x = x
self.y = y
class PointSlots:
__slots__ = ("x", "y") # 声明允许的属性
def __init__(self, x, y):
self.x = x
self.y = y
pd = PointDict(1, 2)
ps = PointSlots(1, 2)
print(sys.getsizeof(pd.__dict__)) # ~232 bytes
print(sys.getsizeof(ps)) # ~56 bytes(小得多)
# __slots__ 的限制:
# ps.z = 3 # AttributeError:不能动态添加属性
适合场景:创建大量小对象(如游戏中的粒子、数据点)时,__slots__ 可节省 30-50% 内存。
容器类魔术方法
class FrozenList:
# 一个不可修改的列表
def __init__(self, data):
self._data = list(data)
def __len__(self) -> int:
return len(self._data)
def __getitem__(self, index):
return self._data[index] # 支持切片(因为 list 本身支持切片)
def __iter__(self):
return iter(self._data) # 让对象可迭代
def __contains__(self, item) -> bool:
return item in self._data
def __repr__(self) -> str:
return f"FrozenList({self._data!r})"
fl = FrozenList([1, 2, 3, 4, 5])
print(len(fl)) # 5
print(fl[1:3]) # [2, 3](切片)
print(3 in fl) # True
for x in fl: print(x) # 可迭代
运算符重载
class Vector:
def __init__(self, x, y):
self.x, self.y = x, y
def __add__(self, other): # v1 + v2
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other): # v1 - v2
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar): # v * 3
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar): # 3 * v(反向)
return self.__mul__(scalar)
def __neg__(self): # -v
return Vector(-self.x, -self.y)
def __abs__(self): # abs(v)
return (self.x**2 + self.y**2) ** 0.5
def __lt__(self, other): # v1 < v2(按模长)
return abs(self) < abs(other)
def __bool__(self): # bool(v)(零向量为 False)
return self.x != 0 or self.y != 0
def __repr__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(2 * v1) # Vector(2, 4)
print(abs(v2)) # 5.0
上下文管理器:enter / exit
class DatabaseConnection:
def __init__(self, url: str):
self.url = url
self.conn = None
def __enter__(self):
print(f"Connecting to {self.url}")
self.conn = {"connected": True} # 模拟连接
return self.conn
def __exit__(self, exc_type, exc_val, exc_tb):
print("Closing connection")
self.conn = None
# 返回 True 表示吞掉异常;返回 False/None 表示异常继续传播
return False
with DatabaseConnection("postgresql://localhost/db") as conn:
print(f"Got connection: {conn}")
描述符协议
描述符是 Python 属性访问机制的底层实现,@property、@classmethod、@staticmethod 都是描述符。
class Validator:
# 可复用的属性验证描述符
def __set_name__(self, owner, name):
self.name = name
self.private_name = "_" + name
def __get__(self, obj, objtype=None):
if obj is None:
return self # 通过类访问时返回描述符本身
return getattr(obj, self.private_name, None)
def __set__(self, obj, value):
self.validate(value)
setattr(obj, self.private_name, value)
def validate(self, value):
pass # 子类实现
class PositiveNumber(Validator):
def validate(self, value):
if not isinstance(value, (int, float)) or value <= 0:
raise ValueError(f"{self.name} must be positive, got {value!r}")
class Circle:
radius = PositiveNumber()
def __init__(self, radius):
self.radius = radius # 触发 PositiveNumber.__set__
c = Circle(5) # OK
# Circle(-1) # ValueError!
抽象类(ABC)
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float:
pass # 子类必须实现
@abstractmethod
def perimeter(self) -> float:
pass # 子类必须实现
def describe(self) -> str:
return f"Shape with area={self.area():.2f}, perimeter={self.perimeter():.2f}"
class Rectangle(Shape):
def __init__(self, w, h):
self.w, self.h = w, h
def area(self) -> float:
return self.w * self.h
def perimeter(self) -> float:
return 2 * (self.w + self.h)
# Shape() # TypeError:不能实例化抽象类
rect = Rectangle(3, 4)
print(rect.describe()) # Shape with area=12.00, perimeter=14.00
dataclass:告别样板代码
from dataclasses import dataclass, field
from typing import List
@dataclass
class Point:
x: float
y: float
def distance_to_origin(self) -> float:
return (self.x**2 + self.y**2) ** 0.5
@dataclass(frozen=True) # 不可变(自动生成 __hash__)
class ImmutablePoint:
x: float
y: float
@dataclass
class Team:
name: str
members: List[str] = field(default_factory=list) # 可变默认值必须用 field
captain: str = ""
def __post_init__(self):
# 初始化后处理
if self.captain and self.captain not in self.members:
self.members.append(self.captain)
# dataclass 自动生成:__init__、__repr__、__eq__
p = Point(3, 4)
print(p) # Point(x=3, y=4)
print(p.distance_to_origin()) # 5.0
综合示例:自定义链表容器
from typing import Iterator, TypeVar, Generic, Optional
T = TypeVar("T")
class LinkedList(Generic[T]):
# 链表:可迭代、支持 with 语句、支持 + 运算
class Node:
__slots__ = ("value", "next")
def __init__(self, value):
self.value = value
self.next = None
def __init__(self):
self._head = None
self._size = 0
def append(self, value) -> None:
node = self.Node(value)
if not self._head:
self._head = node
else:
cur = self._head
while cur.next:
cur = cur.next
cur.next = node
self._size += 1
def __len__(self) -> int:
return self._size
def __iter__(self):
cur = self._head
while cur:
yield cur.value
cur = cur.next
def __contains__(self, value) -> bool:
return any(v == value for v in self)
def __add__(self, other):
result = LinkedList()
for v in self: result.append(v)
for v in other: result.append(v)
return result
def __repr__(self) -> str:
return f"LinkedList([{', '.join(repr(v) for v in self)}])"
def __enter__(self):
return self
def __exit__(self, *args) -> bool:
self._head = None
self._size = 0
return False
# 使用
with LinkedList() as ll:
ll.append(1)
ll.append(2)
ll.append(3)
print(ll) # LinkedList([1, 2, 3])
print(2 in ll) # True
# 退出 with:链表自动清空