1.3. 面向对象编程

Python 的 OOP 有其独特之处,理解这些特性能帮助你写出更优雅的代码。

1.3.1. 类与实例

1.3.1.1. 类属性 vs 实例属性

class Dog:
    species = "Canis familiaris"  # 类属性,所有实例共享
    
    def __init__(self, name):
        self.name = name  # 实例属性,每个实例独立

buddy = Dog("Buddy")
miles = Dog("Miles")

print(buddy.species)  # Canis familiaris
print(miles.species)  # Canis familiaris

# ⚠️ 易错点:通过实例修改类属性
buddy.species = "Canis lupus"  # 创建实例属性,遮蔽类属性
print(buddy.species)  # Canis lupus
print(miles.species)  # Canis familiaris - 不受影响
print(Dog.species)    # Canis familiaris - 类属性未变

# 正确修改类属性
Dog.species = "Modified"
print(miles.species)  # Modified

1.3.1.2. 可变类属性陷阱

# ❌ 危险:可变类属性被所有实例共享
class BadStudent:
    grades = []  # 所有实例共享同一个列表!
    
    def add_grade(self, grade):
        self.grades.append(grade)

s1 = BadStudent()
s2 = BadStudent()
s1.add_grade(90)
print(s2.grades)  # [90] - s2 也有了 s1 的成绩!

# ✅ 正确:在 __init__ 中初始化可变属性
class GoodStudent:
    def __init__(self):
        self.grades = []  # 每个实例独立的列表
    
    def add_grade(self, grade):
        self.grades.append(grade)

1.3.2. 继承与组合

1.3.2.1. 方法解析顺序(MRO)

class A:
    def method(self):
        print("A")

class B(A):
    def method(self):
        print("B")
        super().method()

class C(A):
    def method(self):
        print("C")
        super().method()

class D(B, C):
    def method(self):
        print("D")
        super().method()

d = D()
d.method()
# D
# B
# C
# A

# 查看 MRO
print(D.__mro__)
# (<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)

小技巧

理解 MRO 对于正确使用 super() 至关重要。Python 使用 C3 线性化算法确保每个类只被调用一次。

1.3.2.2. 组合优于继承

# ❌ 过度使用继承
class Car(Engine, Wheels, Seats):  # 多重继承复杂度高
    pass

# ✅ 使用组合
class Engine:
    def start(self):
        return "Engine started"

class Car:
    def __init__(self):
        self.engine = Engine()  # 组合
    
    def start(self):
        return self.engine.start()

# 更灵活:可以在运行时替换组件
class ElectricEngine:
    def start(self):
        return "Electric engine started silently"

electric_car = Car()
electric_car.engine = ElectricEngine()
print(electric_car.start())  # Electric engine started silently

1.3.3. 属性(Property)

1.3.3.1. 使用 @property 实现访问控制

class Temperature:
    def __init__(self, celsius=0):
        self._celsius = celsius  # 内部存储
    
    @property
    def celsius(self):
        """获取摄氏温度"""
        return self._celsius
    
    @celsius.setter
    def celsius(self, value):
        """设置摄氏温度,带验证"""
        if value < -273.15:
            raise ValueError("Temperature below absolute zero!")
        self._celsius = value
    
    @property
    def fahrenheit(self):
        """计算属性:华氏温度"""
        return self._celsius * 9/5 + 32
    
    @fahrenheit.setter
    def fahrenheit(self, value):
        self.celsius = (value - 32) * 5/9

temp = Temperature(25)
print(temp.celsius)     # 25
print(temp.fahrenheit)  # 77.0

temp.fahrenheit = 100
print(temp.celsius)     # 37.77...

# temp.celsius = -300  # ValueError

1.3.3.2. 只读属性

class Circle:
    def __init__(self, radius):
        self._radius = radius
    
    @property
    def radius(self):
        return self._radius
    
    @property
    def area(self):
        """只读计算属性"""
        import math
        return math.pi * self._radius ** 2
    
    @property
    def diameter(self):
        return self._radius * 2

c = Circle(5)
print(c.area)      # 78.53...
# c.area = 100     # AttributeError: can't set attribute

1.3.4. 描述符

描述符是 property、classmethod、staticmethod 的底层机制。

class Validator:
    """数据验证描述符"""
    
    def __init__(self, min_value=None, max_value=None):
        self.min_value = min_value
        self.max_value = max_value
    
    def __set_name__(self, owner, name):
        self.name = name
        self.private_name = f'_{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):
        if self.min_value is not None and value < self.min_value:
            raise ValueError(f"{self.name} must be >= {self.min_value}")
        if self.max_value is not None and value > self.max_value:
            raise ValueError(f"{self.name} must be <= {self.max_value}")
        setattr(obj, self.private_name, value)

class Product:
    price = Validator(min_value=0)
    quantity = Validator(min_value=0, max_value=1000)
    
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

product = Product("Widget", 9.99, 100)
# product.price = -1  # ValueError: price must be >= 0
# product.quantity = 2000  # ValueError: quantity must be <= 1000

1.3.5. 抽象基类

from abc import ABC, abstractmethod

class Shape(ABC):
    """形状抽象基类"""
    
    @abstractmethod
    def area(self):
        """计算面积"""
        pass
    
    @abstractmethod
    def perimeter(self):
        """计算周长"""
        pass
    
    def describe(self):
        """具体方法"""
        return f"Area: {self.area()}, Perimeter: {self.perimeter()}"

# shape = Shape()  # TypeError: Can't instantiate abstract class

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height
    
    def perimeter(self):
        return 2 * (self.width + self.height)

rect = Rectangle(3, 4)
print(rect.describe())  # Area: 12, Perimeter: 14

1.3.6. 数据类(Python 3.7+)

from dataclasses import dataclass, field
from typing import List

@dataclass
class Point:
    x: float
    y: float
    
    def distance_from_origin(self):
        return (self.x ** 2 + self.y ** 2) ** 0.5

# 自动生成 __init__, __repr__, __eq__
p1 = Point(3, 4)
p2 = Point(3, 4)
print(p1)        # Point(x=3, y=4)
print(p1 == p2)  # True

@dataclass
class Student:
    name: str
    grades: List[int] = field(default_factory=list)  # 可变默认值
    gpa: float = field(init=False)  # 不在 __init__ 中
    
    def __post_init__(self):
        if self.grades:
            self.gpa = sum(self.grades) / len(self.grades)
        else:
            self.gpa = 0.0

@dataclass(frozen=True)  # 不可变
class FrozenPoint:
    x: float
    y: float

fp = FrozenPoint(1, 2)
# fp.x = 3  # FrozenError

1.3.7. 元类

元类是"类的类",用于控制类的创建过程。

class SingletonMeta(type):
    """单例元类"""
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=SingletonMeta):
    def __init__(self):
        self.connection = "Connected"

db1 = Database()
db2 = Database()
print(db1 is db2)  # True

# 更简单的单例实现(推荐)
class SimpleSingleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

1.3.7.1. 使用 __init_subclass__ 替代简单元类

class PluginBase:
    """插件注册基类(Python 3.6+)"""
    _plugins = {}
    
    def __init_subclass__(cls, plugin_name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        if plugin_name:
            cls._plugins[plugin_name] = cls
    
    @classmethod
    def get_plugin(cls, name):
        return cls._plugins.get(name)

class JSONPlugin(PluginBase, plugin_name="json"):
    def process(self, data):
        return f"JSON: {data}"

class XMLPlugin(PluginBase, plugin_name="xml"):
    def process(self, data):
        return f"XML: {data}"

print(PluginBase._plugins)  # {'json': <class 'JSONPlugin'>, 'xml': <class 'XMLPlugin'>}
plugin = PluginBase.get_plugin("json")()
print(plugin.process("data"))  # JSON: data

1.3.8. 最佳实践

设计原则
  1. 优先使用组合而非继承

  2. 保持类的职责单一

  3. 使用数据类减少样板代码

  4. 避免深层继承层次

访问控制
  1. 使用 _ 前缀表示内部属性(约定,非强制)

  2. 使用 @property 实现受控访问

  3. 避免使用 __ 双下划线(除非确实需要名称改编)

常见错误
  1. 可变类属性被所有实例共享

  2. 忘记调用 super().__init__()

  3. 多重继承时的钻石问题

  4. 混淆类方法和静态方法