面向对象编程
大约 10 分钟约 2977 字
面向对象编程
简介
Python 支持完整的面向对象编程(OOP),包括类、继承、多态、封装和鸭子类型。Python 的 OOP 比 C# 更灵活,支持多重继承、魔术方法和动态属性。
特点
类定义
基本类
class User:
"""用户类""" # 文档字符串
# 类变量
total_users = 0
def __init__(self, name: str, email: str):
# 实例变量
self.name = name
self.email = email
self._active = True # 约定私有(单下划线)
User.total_users += 1
def greet(self) -> str:
return f"Hello, I'm {self.name}"
def deactivate(self):
self._active = False
def __str__(self) -> str:
return f"User({self.name}, {self.email})"
def __repr__(self) -> str:
return f"User(name='{self.name}', email='{self.email}')"
# 使用
user = User("张三", "zhang@example.com")
print(user.greet()) # Hello, I'm 张三
print(user) # User(张三, zhang@example.com)
print(User.total_users) # 1属性(Property)
class Product:
def __init__(self, name: str, price: float):
self.name = name
self._price = price
self._discount = 0.0
@property
def price(self) -> float:
"""实际价格(考虑折扣)"""
return self._price * (1 - self._discount)
@price.setter
def price(self, value: float):
if value < 0:
raise ValueError("价格不能为负数")
self._price = value
@property
def discount(self) -> float:
return self._discount
@discount.setter
def discount(self, value: float):
if not 0 <= value <= 1:
raise ValueError("折扣必须在 0-1 之间")
self._discount = value
# 使用
product = Product("笔记本电脑", 5999.0)
product.discount = 0.1 # 9折
print(product.price) # 5399.1
# product.price = -100 # ValueError继承
单继承
class Animal:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
def speak(self) -> str:
return "..."
def __str__(self):
return f"{self.__class__.__name__}({self.name}, {self.age})"
class Dog(Animal):
def __init__(self, name: str, age: int, breed: str):
super().__init__(name, age)
self.breed = breed
def speak(self) -> str:
return "汪汪!"
class Cat(Animal):
def speak(self) -> str:
return "喵喵~"
# 多态
animals = [Dog("旺财", 3, "金毛"), Cat("咪咪", 2)]
for animal in animals:
print(f"{animal.name}: {animal.speak()}")
# isinstance / issubclass
print(isinstance(animals[0], Animal)) # True
print(issubclass(Dog, Animal)) # True多重继承与 Mixin
class JSONMixin:
def to_json(self) -> str:
import json
return json.dumps(self.__dict__, ensure_ascii=False, default=str)
class TimestampMixin:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
from datetime import datetime
self.created_at = datetime.now()
def age_in_seconds(self) -> float:
from datetime import datetime
return (datetime.now() - self.created_at).total_seconds()
class Article(TimestampMixin, JSONMixin):
def __init__(self, title: str, content: str):
# super() 按 MRO 顺序调用
super().__init__()
self.title = title
self.content = content
# 使用 Mixin
article = Article("Python OOP", "面向对象编程...")
print(article.to_json())
print(f"创建于: {article.created_at}")
# MRO(方法解析顺序)
print(Article.__mro__)
# (Article, TimestampMixin, JSONMixin, object)魔术方法
常用魔术方法
class Vector:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar: float):
return Vector(self.x * scalar, self.y * scalar)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __lt__(self, other):
return self.magnitude < other.magnitude
def __len__(self):
return int((self.x ** 2 + self.y ** 2) ** 0.5)
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector(x={self.x}, y={self.y})"
def __iter__(self):
yield self.x
yield self.y
@property
def magnitude(self) -> float:
return (self.x ** 2 + self.y ** 2) ** 0.5
# 运算符重载
v1 = Vector(3, 4)
v2 = Vector(1, 2)
print(v1 + v2) # Vector(4, 6)
print(v1 * 2) # Vector(6, 8)
print(v1 == Vector(3, 4)) # True
x, y = v1 # 解包上下文管理器
import time
class Timer:
def __enter__(self):
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.elapsed = time.time() - self.start
print(f"耗时: {self.elapsed:.3f}s")
return False # 不吞掉异常
# 使用 with 语句
with Timer():
time.sleep(1)
# 耗时: 1.001s
# 数据库连接示例
class DatabaseConnection:
def __init__(self, connection_string: str):
self.connection_string = connection_string
def __enter__(self):
print(f"连接数据库: {self.connection_string}")
# self.conn = create_connection(...)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
print("关闭数据库连接")
# self.conn.close()
if exc_type:
print(f"发生异常: {exc_val}")
# 回滚事务
return False
with DatabaseConnection("server=localhost;db=test") as db:
print("执行查询...")数据类(dataclass)
Python 3.7+ 数据类
from dataclasses import dataclass, field
from typing import List
@dataclass
class OrderItem:
product: str
quantity: int
price: float
@property
def total(self) -> float:
return self.quantity * self.price
@dataclass
class Order:
id: int
customer: str
items: List[OrderItem] = field(default_factory=list)
status: str = "pending"
def add_item(self, product: str, quantity: int, price: float):
self.items.append(OrderItem(product, quantity, price))
@property
def total_amount(self) -> float:
return sum(item.total for item in self.items)
def __post_init__(self):
if not self.items:
self.status = "empty"
# 使用
order = Order(1, "张三")
order.add_item("Python 书", 2, 89.0)
order.add_item("键盘", 1, 299.0)
print(order) # Order(id=1, customer='张三', items=[...], status='empty')
print(f"总价: ¥{order.total_amount:.2f}") # 总价: ¥477.00高级 dataclass 用法
from dataclasses import dataclass, field, InitVar
from typing import List, Dict, Optional, ClassVar
from datetime import datetime
import json
@dataclass
class ConfigurableModel:
"""带配置的高级 dataclass"""
# ClassVar: 类变量,不在实例中
DEFAULT_TIMEOUT: ClassVar[int] = 30
MAX_RETRIES: ClassVar[int] = 3
name: str
timeout: int = DEFAULT_TIMEOUT
retries: int = MAX_RETRIES
tags: List[str] = field(default_factory=list)
metadata: Dict[str, str] = field(default_factory=dict)
created_at: datetime = field(default_factory=datetime.now)
# InitVar: 仅用于 __init__ 的参数,不存储为字段
_validate: InitVar[bool] = True
def __post_init__(self, _validate: bool):
if _validate:
if self.timeout < 1:
raise ValueError("timeout 不能小于 1")
if self.retries < 0:
raise ValueError("retries 不能为负数")
def to_dict(self) -> dict:
from dataclasses import asdict
return asdict(self)
def to_json(self) -> str:
return json.dumps(self.to_dict(), ensure_ascii=False, default=str, indent=2)
# 不可变 dataclass
@dataclass(frozen=True)
class ImmutablePoint:
"""不可变对象(线程安全)"""
x: float
y: float
def distance_to(self, other: "ImmutablePoint") -> float:
return ((self.x - other.x) ** 2 + (self.y - other.y) ** 2) ** 0.5
p1 = ImmutablePoint(3, 4)
p2 = ImmutablePoint(0, 0)
print(p1.distance_to(p2)) # 5.0
# p1.x = 10 # FrozenInstanceError
# 带排序的 dataclass
@dataclass(order=True)
class Version:
"""可排序的 dataclass"""
major: int
minor: int
patch: int = 0
versions = [Version(1, 0), Version(2, 1), Version(1, 5)]
versions.sort()
print(versions) # [Version(major=1, minor=0, ...), Version(major=1, minor=5, ...), ...]描述符协议深入
class LazyProperty:
"""惰性计算属性(只计算一次)"""
def __init__(self, func):
self.func = func
self.attr_name = func.__name__
def __get__(self, obj, objtype=None):
if obj is None:
return self
value = self.func(obj)
setattr(obj, self.attr_name, value) # 缓存结果
return value
def __set_name__(self, owner, name):
self.attr_name = name
class HeavyComputation:
"""使用惰性属性避免不必要的计算"""
def __init__(self, data: list[int]):
self.data = data
@LazyProperty
def sorted_data(self) -> list[int]:
print("执行排序计算...")
return sorted(self.data)
@LazyProperty
def statistics(self) -> dict:
print("执行统计计算...")
d = self.sorted_data # 依赖另一个惰性属性
return {
"min": d[0],
"max": d[-1],
"mean": sum(d) / len(d),
}
# 第一次访问触发计算
hc = HeavyComputation([3, 1, 4, 1, 5, 9, 2, 6])
print(hc.statistics) # 排序和统计都计算
print(hc.statistics) # 直接返回缓存,不再计算
# 类型检查描述符
class Validated:
"""通用类型检查描述符"""
def __init__(self, type_hint, *, min_val=None, max_val=None):
self.type_hint = type_hint
self.min_val = min_val
self.max_val = max_val
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
if obj is None:
return self
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
if not isinstance(value, self.type_hint):
raise TypeError(
f"{self.name} 期望 {self.type_hint.__name__},收到 {type(value).__name__}"
)
if self.min_val is not None and value < self.min_val:
raise ValueError(f"{self.name} 不能小于 {self.min_val}")
if self.max_val is not None and value > self.max_val:
raise ValueError(f"{self.name} 不能大于 {self.max_val}")
obj.__dict__[self.name] = value
class Product:
name = Validated(str, min_val=1) # 非空字符串
price = Validated((int, float), min_val=0)
stock = Validated(int, min_val=0)
p = Product()
p.name = "Python 书"
p.price = 89.9
# p.price = -10 # ValueError
# p.name = 123 # TypeError抽象基类与协议
from abc import ABC, abstractmethod
from typing import Protocol, runtime_checkable
# 方式 1:ABC 抽象基类
class DataProcessor(ABC):
"""数据处理器抽象基类"""
@abstractmethod
def process(self, data: list[dict]) -> list[dict]:
"""处理数据"""
pass
@abstractmethod
def validate(self, data: list[dict]) -> bool:
"""验证数据"""
pass
def run(self, data: list[dict]) -> list[dict]:
"""模板方法:定义算法骨架"""
if not self.validate(data):
raise ValueError("数据验证失败")
return self.process(data)
class CSVDataProcessor(DataProcessor):
def process(self, data: list[dict]) -> list[dict]:
return [{k: str(v).strip() for k, v in row.items()} for row in data]
def validate(self, data: list[dict]) -> bool:
return isinstance(data, list) and len(data) > 0
# 方式 2:Protocol 结构化子类型(Python 3.8+)
@runtime_checkable
class Drawable(Protocol):
def draw(self) -> str:
...
def resize(self, scale: float) -> None:
...
class Circle:
def draw(self) -> str:
return "绘制圆形"
def resize(self, scale: float) -> None:
print(f"缩放圆形: {scale}x")
class Rectangle:
def draw(self) -> str:
return "绘制矩形"
def resize(self, scale: float) -> None:
print(f"缩放矩形: {scale}x")
def render_shapes(shapes: list[Drawable]):
"""只要实现了 draw 和 resize 方法就能通过"""
for shape in shapes:
print(shape.draw())
shape.resize(1.5)
render_shapes([Circle(), Rectangle()])
# isinstance 检查(Protocol 支持)
print(isinstance(Circle(), Drawable)) # True设计模式实战
from typing import Optional, Dict, Any
from dataclasses import dataclass, field
from datetime import datetime
# 策略模式
class PricingStrategy:
"""定价策略接口"""
def calculate(self, base_price: float, quantity: int) -> float:
raise NotImplementedError
class RegularPricing(PricingStrategy):
def calculate(self, base_price: float, quantity: int) -> float:
return base_price * quantity
class VIPPricing(PricingStrategy):
def calculate(self, base_price: float, quantity: int) -> float:
total = base_price * quantity
return total * 0.85 if total > 100 else total * 0.95
class WholesalePricing(PricingStrategy):
def calculate(self, base_price: float, quantity: int) -> float:
if quantity >= 100:
return base_price * quantity * 0.7
elif quantity >= 50:
return base_price * quantity * 0.8
return base_price * quantity
@dataclass
class Order:
items: list[Dict[str, Any]] = field(default_factory=list)
_strategy: Optional[PricingStrategy] = field(default=None, repr=False)
@property
def strategy(self) -> PricingStrategy:
return self._strategy or RegularPricing()
@strategy.setter
def strategy(self, value: PricingStrategy):
self._strategy = value
def total(self) -> float:
return sum(
self.strategy.calculate(item["price"], item["quantity"])
for item in self.items
)
# 使用
order = Order(items=[
{"name": "商品A", "price": 50, "quantity": 3},
{"name": "商品B", "price": 120, "quantity": 1},
])
print(f"普通价: {order.total():.2f}") # 270.00
order.strategy = VIPPricing()
print(f"VIP 价: {order.total():.2f}") # 229.50
# 观察者模式
class EventEmitter:
"""简单事件系统"""
def __init__(self):
self._listeners: Dict[str, list] = {}
def on(self, event: str, callback):
self._listeners.setdefault(event, []).append(callback)
def off(self, event: str, callback):
if event in self._listeners:
self._listeners[event] = [c for c in self._listeners[event] if c != callback]
def emit(self, event: str, *args, **kwargs):
for callback in self._listeners.get(event, []):
callback(*args, **kwargs)
bus = EventEmitter()
bus.on("order_created", lambda order_id: print(f"通知: 订单 {order_id} 已创建"))
bus.on("order_created", lambda order_id: print(f"日志: 订单 {order_id} 已创建"))
bus.emit("order_created", "ORD-001")优点
缺点
总结
Python OOP 核心:class 定义类,__init__ 初始化,self 引用实例。@property 定义属性访问器。继承用 super() 调用父类。Mixin 实现多重继承组合功能。魔术方法(__str__, __add__, __enter__)自定义行为。dataclass 简化数据类定义。鸭子类型关注对象行为而非类型,与 C# 的接口多态理念不同。
关键知识点
- 先区分这篇内容属于语法能力、工程能力,还是生态工具能力。
- Python 的开发效率来自生态,但可维护性来自结构、测试和规范。
- 脚本一旦进入长期维护,就必须按项目来治理。
- 框架与语言特性类主题要同时理解运行方式和工程组织方式。
项目落地视角
- 统一虚拟环境、依赖锁定、格式化和日志方案。
- 把入口、配置、业务逻辑和工具函数拆开,避免单文件膨胀。
- 对网络请求、文件读写和数据处理结果做异常与样本校验。
- 明确项目入口、配置管理、依赖管理、日志和测试策略。
常见误区
- 把临时脚本直接当生产代码使用。
- 忽略依赖版本、编码、路径和时区差异。
- 只会写 happy path,没有补超时、重试和资源释放。
- 把 notebook 或脚本风格直接带入长期维护项目。
进阶路线
- 把类型注解、测试、打包和部署纳入统一工程流程。
- 继续向异步、性能、数据管线和框架源码层深入。
- 把常用脚本抽成可复用库或 CLI 工具,而不是复制粘贴。
- 继续补齐部署、打包、监控和性能调优能力。
适用场景
- 当你准备把《面向对象编程》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合脚本自动化、数据处理、Web 开发和测试工具建设。
- 当需求强调快速迭代和丰富生态时,Python 往往能快速起步。
落地建议
- 统一使用虚拟环境与依赖锁定,避免环境漂移。
- 对核心函数补类型注解、异常处理和日志,减少“脚本黑盒”。
- 一旦脚本进入生产链路,及时补测试和监控。
排错清单
- 先确认当前解释器、虚拟环境和依赖版本是否正确。
- 检查编码、路径、时区和第三方库行为差异。
- 排查同步阻塞、数据库连接未释放或网络请求无超时。
复盘问题
- 如果把《面向对象编程》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《面向对象编程》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《面向对象编程》最大的收益和代价分别是什么?
