1. 面向对象是什么? #
- 面向对象(OOP)用 类(class) 描述一类事物的共同特征与行为。
- 对象(object) 是类创建出来的具体实例,例如
User("Alice", "a@example.com")。
- 全栈项目里大量代码以类组织:Django Model、DRF Serializer、业务 Service、自定义异常类等。
- 框架已帮你搭好类结构,业务开发主要是「继承 + 填字段 + 写方法」。
- 核心习惯:把数据和行为放在同一个类里,通过方法对外暴露能力,而不是在全局用裸字典到处改字段。
- 这样职责清晰、便于测试和协作。
2. 类与对象 #
- 类是「模板」,对象是「按模板造出来的实体」。
- 定义类用
class 类名:,创建对象类似调用类名 User(...),会自动执行 __init__ 完成初始化。
| 要点 |
说明 |
__init__ |
构造方法,创建对象时自动执行 |
self |
代表当前实例,定义实例方法时第一个参数;调用时不用传 |
| 方法 |
函数写在类里,第一个参数为 self |
class User:
def __init__(self, name: str, email: str):
self.name = name
self.email = email
def display(self) -> str:
return f"{self.name} <{self.email}>"
user = User("Alice", "a@example.com")
print(user.display())
2.1 实例属性 vs 类属性 #
- 实例属性(
self.xxx):每个对象各自一份,如每个订单的金额、每个用户的邮箱。
- 类属性(直接写在类体、不在
__init__ 里):所有实例共享同一份,如全局税率、默认配置。
业务数据(金额、状态、用户 ID)一律用实例属性;只有「整类共享、且不随实例变化」的配置才考虑类属性。
class Order:
tax_rate = 0.06
def __init__(self, amount: float):
self.amount = amount
3. 封装 #
- 封装指把内部实现细节藏起来,对外只提供稳定、安全的接口。
- 调用方不必知道余额存在
_balance 里,只需调用 withdraw() 和读 balance。
- Python 没有 Java 式的
private 关键字,只能靠命名约定和装饰器表达封装意图。
- 单下划线
_balance:约定为「内部使用」,不应被外部直接访问(技术上仍可访问)。
- 双下划线
__name:名称重整(name mangling),弱化外部访问(仍可通过 _类名__name 访问),多用于避免子类冲突。
@property:把方法伪装成属性访问,读取时可加校验或逻辑。
- **`@property.setter
**:写入属性时触发校验,如 **account.balance = 1000`** 实际调用 setter。
class Account:
def __init__(self, balance: float):
self._balance = balance
self.__private_name = "AccountUser"
def withdraw(self, amount: float) -> bool:
if amount > self._balance:
return False
self._balance -= amount
return True
@property
def balance(self) -> float:
return self._balance
@balance.setter
def balance(self, value: float):
if value < 0:
raise ValueError("余额不能为负数")
self._balance = value
def get_private_name(self):
return self.__private_name
account = Account(1000)
print(account.balance)
account.balance = 500
print(account.balance)
try:
print(account.__private_name)
except AttributeError:
print("无法直接通过 account.__private_name 访问(名称被重整)")
print("通过名称重整访问双下划线属性:", account._Account__private_name)
print("通过方法访问双下划线属性:", account.get_private_name())
try:
account.balance = -100
except ValueError as e:
print("设置余额为负时抛出异常:", e)
print(account.balance)
4. 继承与 super() #
- 继承让子类复用父类的字段和方法,并可重写(override)父类方法以改变行为;写法:
class 子类(父类):。
- 扩展父类时,
__init__ 里应先用 super().__init__(...) 调用父类构造,再初始化子类特有字段。
- 框架中的典型写法是继承
models.Model、APIView 等基类,只声明本表字段和业务逻辑。
- 多重继承(
class C(A, B))Python 支持,按 MRO 决定方法查找顺序。
- 业务代码尽量少写复杂多继承,框架提供的 Mixin 够用即可。
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}: 汪"
dog = Dog("旺财")
print(dog.speak())
5. 多态 #
- 多态指:不同类可以有同名方法,但实现不同。
- 调用方只要求「对象有这个方法」,不关心具体类型。
- 例如邮件、短信通知都实现
send_message(),统一用 notify(entity) 调用,无需写 if isinstance(...)。
- Web 层「视图只调
service.create_order()」也是类似思想——依赖接口能力,而非具体类名。
class EmailNotifier:
def send_message(self):
return "Sending email notification"
class SMSNotifier:
def send_message(self):
return "Sending SMS notification"
class PushNotifier:
def send_message(self):
return "Sending push notification"
def notify(entity):
print(entity.send_message())
notifiers = [EmailNotifier(), SMSNotifier(), PushNotifier()]
for n in notifiers:
notify(n)
6. 常用魔术方法 #
- 以双下划线开头结尾的方法(如
__init__)由 Python 在特定时机自动调用,称为魔术方法或特殊方法。
- 业务 Model 最常写的是
__init__ 和 __str__。
| 方法 |
何时触发 |
项目用途 |
__init__ |
创建对象 |
初始化字段 |
__str__ |
print(obj) |
日志、调试友好显示 |
__repr__ |
交互环境显示 |
开发排查 |
class Order:
def __init__(self, order_id: int):
self.order_id = order_id
def __str__(self) -> str:
return f"Order({self.order_id})"
order = Order(12345)
print(order)
7. 类方法与静态方法 #
- 除了普通实例方法(第一个参数是
self),类还可以有类方法和静态方法。
- 实例方法:第一个参数
self,读写实例状态。
@classmethod:第一个参数 cls(类本身),常用作工厂方法,如 User.from_dict(data)。
@staticmethod:无隐式第一个参数,挂在类名下的工具函数;若完全独立,也可写成模块级函数。
| 类型 |
第一个参数 |
典型场景 |
| 实例方法 |
self |
读写实例状态 |
@classmethod |
cls(类本身) |
工厂方法、从字典/JSON 构造对象 |
@staticmethod |
无 |
挂在类名下的工具函数;也可放模块级函数 |
class User:
def __init__(self, name: str):
self.name = name
@classmethod
def from_dict(cls, data: dict) -> "User":
return cls(data["name"])
@staticmethod
def is_valid_email(email: str) -> bool:
return "@" in email
user = User("Alice")
print(user.name)
print(user.from_dict({"name": "Bob"}))
print(user.is_valid_email("test@example.com"))
8. 抽象基类 #
- 抽象基类(ABC) 约定子类必须实现某些方法,否则无法实例化。
- 适合支付渠道、存储后端、插件等「多种实现、同一接口」的扩展点。
- 用
abc.ABC 和 @abstractmethod 标记接口;具体子类(如 Alipay)实现 pay() 等方法。
from abc import ABC, abstractmethod
class Payment(ABC):
@abstractmethod
def pay(self, amount: float) -> str:
pass
class Alipay(Payment):
def pay(self, amount: float) -> str:
return "ok"
alipay = Alipay()
print(alipay.pay(100))
9. 上下文管理器 #
- 实现
__enter__ 和 __exit__ 的类,可配合 with 语句使用。
- 进入
with 块时执行 __enter__,离开时(含异常)执行 __exit__,用于自动释放资源。
- 文件读写已用
with open(...),底层就是上下文管理器。
- 自定义数据库连接、锁、临时资源时偶尔编写。
class FileManager:
def __init__(self, filename, mode):
self.filename = filename
self.mode = mode
self.file = None
def __enter__(self):
self.file = open(self.filename, self.mode, encoding="utf-8")
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
with FileManager("test.txt", "w") as f:
f.write("Hello, world!")
10. 项目开发要点 #
- Model / Entity:字段 + 少量业务方法;复杂流程放到 Service 类。
- 继承框架基类(
models.Model、APIView)而不是从零造轮子。
- 用
super().__init__(...) 扩展父类初始化。
- 封装:敏感字段用
_xxx,对外用方法或 @property。
- 日志与调试:实现
__str__,便于打印对象。
- 少做多重继承:一个主父类 + 框架提供的 Mixin 即可。
- 能函数解决的不强行 OOP:工具函数、模块同样清晰。
11. 总结 #
- 面向对象核心:类描述模板,对象是实例;封装、继承、多态组织业务代码。
- 全栈项目大量继承框架基类,配合 Service 层拆分复杂流程。
- 下表对照概念与项目中的体现。
| 概念 |
项目中的体现 |
| 类 / 对象 |
Model、Service、Serializer |
__init__ / self |
初始化业务对象 |
| 封装 |
_内部字段、公开方法 |
继承 / super |
扩展 Model、基类视图 |
@classmethod |
工厂、ORM 风格 API |
__str__ |
日志可读 |