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. 最佳实践
设计原则
优先使用组合而非继承
保持类的职责单一
使用数据类减少样板代码
避免深层继承层次
访问控制
使用
_前缀表示内部属性(约定,非强制)使用
@property实现受控访问避免使用
__双下划线(除非确实需要名称改编)
常见错误
可变类属性被所有实例共享
忘记调用
super().__init__()多重继承时的钻石问题
混淆类方法和静态方法