Python 后端开发技术博客专栏 | 第 03 篇 面向对象编程进阶 -- 从 SOLID 原则到 Python 特色 OOP

难度等级: 中级-高级
适合读者: 有 Python 基础的开发者,准备面试的中高级工程师
前置知识: 第 01 篇《Python 数据结构全解析》、第 02 篇《函数式编程与 Python 魔法》


导读

面向对象编程(OOP)是构建复杂系统的基石。但在 Python 的世界里,OOP 有着鲜明的自身特色 -- 它不像 Java 那样强制一切皆对象,也不像 C++ 那样提供 public/protected/private 关键字。Python 的 OOP 哲学是:约定优于强制,鸭子类型优于显式接口,简单优于复杂

如果你用过 Django 的 Model 继承体系,就见识过 __new__ 和元类的威力;如果你写过 FastAPI 的依赖注入,就实践过依赖倒置原则;如果你在代码审查中遇到过继承层次深达 5 层以上的"继承地狱",就能理解为什么"组合优于继承"是如此重要。

但在面试和实际开发中,很多人会在以下问题上卡壳:

  • __new____init__ 到底谁先执行?什么时候需要重写 __new__
  • Python 的 MRO 是什么?多重继承时 super() 到底调用的是哪个父类的方法?
  • 鸭子类型和 ABC 抽象基类的适用场景分别是什么?
  • SOLID 原则在 Python 这种动态语言中如何落地?
  • 什么时候用继承,什么时候用组合?Mixin 又是什么?

本文将从 CPython 实现机制到 SOLID 设计原则,再到魔法方法的完整协议体系,系统性地拆解 Python OOP 的高级特性,帮助你在面试和项目中都能游刃有余。


学习目标

读完本文后,你将能够:

  1. 解释 __new____init__ 的分工,理解类的两阶段构造过程,能用 __new__ 实现单例模式和不可变类型
  2. 掌握 MRO 与 C3 线性化算法,准确分析多重继承场景下的方法查找顺序
  3. 理解鸭子类型的核心哲学,区分 ABC 抽象基类与 Protocol 的适用场景
  4. 在 Python 项目中践行 SOLID 五大原则,写出高内聚、低耦合的代码
  5. 识别继承滥用的反模式,掌握组合模式和 Mixin 模式的正确使用方式
  6. 熟练实现常用魔法方法,让自定义类完美融入 Python 生态(可比较、可迭代、可哈希、支持上下文管理)
  7. 在面试中自信回答 OOP 相关的高频问题

一、Python OOP 核心机制

1.1 类的两阶段构造:__new__ vs __init__

在 Python 中,创建一个对象分为两步:分配内存(__new__初始化属性(__init__ 。绝大多数情况下我们只需要写 __init__,但理解 __new__ 是掌握高级 OOP 的关键。

python 复制代码
class MyClass:
    def __new__(cls, *args, **kwargs):
        print(f"1. __new__ 被调用, cls={cls.__name__}")
        instance = super().__new__(cls)  # 创建实例(分配内存)
        print(f"   实例 id: {id(instance)}")
        return instance

    def __init__(self, name: str):
        print(f"2. __init__ 被调用, self id: {id(self)}")
        self.name = name

obj = MyClass("Alice")
# 输出:
# 1. __new__ 被调用, cls=MyClass
#    实例 id: 140234567890
# 2. __init__ 被调用, self id: 140234567890

关键区别

特征 __new__ __init__
方法类型 静态方法(隐式) 实例方法
第一个参数 cls(类本身) self(实例)
职责 创建并返回实例 初始化实例属性
返回值 必须返回实例 无返回值(返回 None)
调用时机 先于 __init__ __new__ 返回实例后

底层原理 :当你调用 MyClass("Alice") 时,CPython 内部执行的逻辑大致是:

python 复制代码
# 伪代码:CPython type.__call__ 的简化逻辑
def __call__(cls, *args, **kwargs):
    instance = cls.__new__(cls, *args, **kwargs)
    if isinstance(instance, cls):
        instance.__init__(*args, **kwargs)  # 仅当 __new__ 返回的是 cls 的实例时
    return instance

注意:如果 __new__ 返回的不是当前类的实例,__init__ 就不会被调用。这个特性在实现某些设计模式时很有用。

实战一:用 __new__ 实现单例模式

python 复制代码
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, value: str = "default"):
        self.value = value

s1 = Singleton("first")
s2 = Singleton("second")
print(s1 is s2)       # True -- 同一个实例
print(s1.value)        # second -- 注意:__init__ 被调用了两次
print(id(s1) == id(s2))  # True

注意__new__ 实现的单例虽然保证了只有一个实例,但 __init__ 每次调用 Singleton() 都会执行,可能重复初始化。生产环境建议加一个标志位或使用元类方式。

实战二:用 __new__ 扩展不可变类型

继承不可变类型(如 intstrtuple)时,必须在 __new__ 中设置值,因为 __init__ 时对象已经创建完毕,不可变类型无法再修改:

python 复制代码
class PositiveInt(int):
    """只允许正整数的 int 子类"""

    def __new__(cls, value: int):
        if value <= 0:
            raise ValueError(f"PositiveInt 必须为正数, 收到: {value}")
        return super().__new__(cls, value)

n = PositiveInt(42)
print(n)           # 42
print(type(n))     # <class '__main__.PositiveInt'>
print(n + 8)       # 50
print(isinstance(n, int))  # True

try:
    PositiveInt(-1)
except ValueError as e:
    print(e)  # PositiveInt 必须为正数, 收到: -1

1.2 MRO(方法解析顺序)与 C3 线性化

Python 支持多重继承,当多个父类有同名方法时,Python 使用 C3 线性化算法 确定方法的查找顺序,这个顺序称为 MRO(Method Resolution Order)

钻石继承问题

python 复制代码
class A:
    def method(self):
        print("A.method")

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

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

class D(B, C):
    pass

d = D()
d.method()  # B.method -- 按 MRO 顺序查找

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

# 也可以用 mro() 方法
print(D.mro())
# [<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>]

C3 线性化算法规则(简化理解):

  1. 子类永远在父类之前

  2. 多个父类的相对顺序保持与类定义中的声明顺序一致

  3. 以上规则递归应用于所有继承层级

    D(B, C) 的 MRO 计算过程:
    L[D] = D + merge(L[B], L[C], [B, C])
    L[B] = B, A, object
    L[C] = C, A, object

    merge([B, A, object], [C, A, object], [B, C])
    → 取 B(B 不在任何其他列表的尾部)
    → merge([A, object], [C, A, object], [C])
    → 取 C(A 在 [C,A,object] 尾部中出现,不能取;C 可以取)
    → merge([A, object], [A, object])
    → 取 A → 取 object

    最终:D → B → C → A → object

无效的 MRO -- Python 会报错

python 复制代码
# 某些继承结构无法线性化,Python 会拒绝
class X(A):
    pass

class Y(A):
    pass

# 如果 X 和 Y 的继承顺序与某个子类声明顺序矛盾,会抛出 TypeError
# 例如:class Z(X, Y, C, B) 如果 B 和 C 的 MRO 冲突

1.3 super() 的正确使用

super() 不是简单地"调用父类方法" -- 它按照 MRO 链调用下一个类的方法。这在多重继承时尤为重要:

python 复制代码
class A:
    def __init__(self):
        print("A.__init__")
        super().__init__()

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

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

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

d = D()
# D.__init__
# B.__init__
# C.__init__    ← super() 在 B 中调用的不是 A,而是 MRO 链的下一个:C
# A.__init__

这就是 协作式多重继承 (cooperative multiple inheritance)的核心:每个类通过 super() 把调用传递给 MRO 链中的下一个,而不是硬编码调用某个特定的父类。

super() 的常见陷阱

python 复制代码
# 错误:混合使用 super() 和直接调用父类
class Base:
    def __init__(self):
        print("Base.__init__")

class Child(Base):
    def __init__(self):
        Base.__init__(self)  # 直接调用,不走 MRO
        # 在多重继承中,这会导致某些类的 __init__ 被跳过或重复调用

# 正确做法:统一使用 super()
class Child(Base):
    def __init__(self):
        super().__init__()  # 遵循 MRO 链

带参数的 super() 调用(多重继承中参数传递的推荐模式):

python 复制代码
class Base:
    def __init__(self, **kwargs):
        # 消费完自己不需要的参数后,剩余的传给下一个
        print(f"Base.__init__ 剩余参数: {kwargs}")
        super().__init__(**kwargs)

class LogMixin:
    def __init__(self, log_level: str = "INFO", **kwargs):
        self.log_level = log_level
        print(f"LogMixin: log_level={log_level}")
        super().__init__(**kwargs)

class CacheMixin:
    def __init__(self, cache_ttl: int = 300, **kwargs):
        self.cache_ttl = cache_ttl
        print(f"CacheMixin: cache_ttl={cache_ttl}")
        super().__init__(**kwargs)

class Service(LogMixin, CacheMixin, Base):
    def __init__(self, name: str, **kwargs):
        self.name = name
        print(f"Service: name={name}")
        super().__init__(**kwargs)

svc = Service(name="api", log_level="DEBUG", cache_ttl=600)
# Service: name=api
# LogMixin: log_level=DEBUG
# CacheMixin: cache_ttl=600
# Base.__init__ 剩余参数: {}

使用 **kwargs 传递参数是协作式多重继承的最佳实践,每个类从 kwargs 中取出自己需要的参数,其余的交给 MRO 链中的下一个类。


二、封装、继承、多态的 Python 实现

2.1 名称改编(Name Mangling)

Python 没有真正的 private 关键字,而是通过命名约定名称改编来实现访问控制:

python 复制代码
class Account:
    def __init__(self, owner: str, balance: float):
        self.owner = owner         # 公开属性
        self._currency = "CNY"     # 约定:受保护(外部可访问,但不建议)
        self.__balance = balance   # 名称改编:外部无法直接访问

    def get_balance(self) -> float:
        return self.__balance

    def _internal_method(self):
        """单下划线:内部使用,但不阻止访问"""
        return "internal"

    def __private_method(self):
        """双下划线:名称改编"""
        return "private"

acc = Account("Alice", 1000.0)

# 公开属性正常访问
print(acc.owner)        # Alice

# 单下划线:约定上是内部使用,但可以访问
print(acc._currency)    # CNY

# 双下划线:名称改编,直接访问会报错
try:
    print(acc.__balance)
except AttributeError as e:
    print(f"AttributeError: {e}")
    # AttributeError: 'Account' object has no attribute '__balance'

# 名称改编的真相:Python 把 __balance 改名为 _Account__balance
print(acc._Account__balance)  # 1000.0

# 查看实例的所有属性
print([attr for attr in dir(acc) if 'balance' in attr.lower()])
# ['_Account__balance', 'get_balance']

名称改编的设计意图 :名称改编不是为了"安全"(它很容易绕过),而是为了避免子类意外覆盖父类的内部属性

python 复制代码
class Base:
    def __init__(self):
        self.__data = "base_data"  # 改编为 _Base__data

    def get_data(self):
        return self.__data

class Child(Base):
    def __init__(self):
        super().__init__()
        self.__data = "child_data"  # 改编为 _Child__data(不同的名字!)

c = Child()
print(c.get_data())     # base_data -- Base 的 __data 没有被覆盖
print(c._Child__data)   # child_data
print(c._Base__data)    # base_data

Python 访问控制的最佳实践

命名方式 含义 真实行为
name 公开 自由访问
_name 受保护(约定) 可以访问,但暗示"内部使用"
__name 私有(名称改编) 改编为 _ClassName__name
__name__ 魔法方法 Python 特殊用途,不要自创

2.2 鸭子类型(Duck Typing)

"如果它走起来像鸭子,叫起来像鸭子,那它就是鸭子。"

Python 的多态不依赖继承,而是依赖行为 -- 只要对象实现了所需的方法,就可以被使用:

python 复制代码
class Duck:
    def quack(self) -> str:
        return "嘎嘎嘎"

    def walk(self) -> str:
        return "摇摇摆摆地走"

class Person:
    def quack(self) -> str:
        return "我在模仿鸭子叫"

    def walk(self) -> str:
        return "大步流星地走"

class RobotDuck:
    def quack(self) -> str:
        return "电子嘎嘎嘎"

    def walk(self) -> str:
        return "机械地移动"

def duck_test(duck) -> None:
    """不关心 duck 是什么类型,只要它能 quack 和 walk"""
    print(f"叫声: {duck.quack()}")
    print(f"走路: {duck.walk()}")

# 三个完全不相关的类,没有任何继承关系,但都能通过鸭子测试
for obj in [Duck(), Person(), RobotDuck()]:
    duck_test(obj)
    print("---")

鸭子类型 vs 显式接口

python 复制代码
from abc import ABC, abstractmethod

# 方式一:鸭子类型(Pythonic)
def process_payment_duck(processor):
    """任何有 charge 方法的对象都可以"""
    return processor.charge(100.0)

# 方式二:ABC 显式接口(严格约束)
class PaymentProcessor(ABC):
    @abstractmethod
    def charge(self, amount: float) -> bool:
        pass

class StripeProcessor(PaymentProcessor):
    def charge(self, amount: float) -> bool:
        print(f"Stripe 收费: {amount}")
        return True

# ABC 会在实例化时强制检查
class BrokenProcessor(PaymentProcessor):
    pass  # 没有实现 charge

try:
    BrokenProcessor()
except TypeError as e:
    print(f"TypeError: {e}")
    # TypeError: Can't instantiate abstract class BrokenProcessor
    #            with abstract method charge

选择建议

场景 推荐方式 原因
内部模块间调用 鸭子类型 灵活、简洁
公开 API / 框架设计 ABC 或 Protocol 强约束、文档性强
需要 isinstance 检查 ABC ABC 支持 register
需要静态类型检查 Protocol(Python 3.8+) 兼顾鸭子类型和类型安全

2.3 Protocol -- 鸭子类型的静态检查(Python 3.8+)

typing.Protocol 实现了结构化子类型:不需要继承,只需要实现相同的方法签名就能通过类型检查:

python 复制代码
from typing import Protocol, runtime_checkable

@runtime_checkable
class Drawable(Protocol):
    """任何有 draw 方法的对象都是 Drawable"""
    def draw(self) -> str: ...

class Circle:
    def draw(self) -> str:
        return "画一个圆"

class Square:
    def draw(self) -> str:
        return "画一个方"

class NotDrawable:
    pass

def render(shape: Drawable) -> None:
    """类型检查器(mypy)会验证传入的对象是否符合 Drawable"""
    print(shape.draw())

# Circle 和 Square 没有继承 Drawable,但它们实现了 draw 方法
render(Circle())   # 画一个圆
render(Square())   # 画一个方

# runtime_checkable 允许运行时 isinstance 检查
print(isinstance(Circle(), Drawable))      # True
print(isinstance(NotDrawable(), Drawable))  # False

Protocol vs ABC

特征 Protocol ABC
是否需要继承 不需要 需要
检查方式 结构化(看方法签名) 名义化(看继承关系)
运行时检查 需要 @runtime_checkable 天然支持 isinstance
主要用途 类型标注、静态检查 定义接口约束
Python 版本 3.8+ 一直都有

三、SOLID 原则在 Python 中的实践

SOLID 是面向对象设计的五大原则。在 Python 这种动态语言中,SOLID 的某些原则需要用更 Pythonic 的方式来落地。

3.1 单一职责原则(SRP)

一个类应该只有一个引起它变化的原因。

python 复制代码
# 违反 SRP:User 类同时负责业务逻辑和数据持久化
class UserBad:
    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email

    def validate(self) -> bool:
        return "@" in self.email and len(self.name) > 0

    def save_to_db(self):
        """持久化逻辑不应该在实体类中"""
        print(f"INSERT INTO users (name, email) VALUES ('{self.name}', '{self.email}')")

    def send_welcome_email(self):
        """发送邮件逻辑也不应该在这里"""
        print(f"Sending email to {self.email}")


# 遵循 SRP:每个类只有一个职责
class User:
    """实体类:只负责数据和业务规则"""
    def __init__(self, name: str, email: str):
        self.name = name
        self.email = email

    def validate(self) -> bool:
        return "@" in self.email and len(self.name) > 0

class UserRepository:
    """数据访问层:只负责持久化"""
    def save(self, user: User) -> None:
        if not user.validate():
            raise ValueError("用户数据无效")
        print(f"INSERT INTO users (name, email) VALUES ('{user.name}', '{user.email}')")

class EmailService:
    """通知服务:只负责发送邮件"""
    def send_welcome(self, user: User) -> None:
        print(f"Sending welcome email to {user.email}")

# 使用
user = User("Alice", "alice@example.com")
repo = UserRepository()
email_svc = EmailService()

repo.save(user)
email_svc.send_welcome(user)

3.2 开放封闭原则(OCP)

对扩展开放,对修改封闭 -- 新增功能时不需要修改已有代码。

python 复制代码
from abc import ABC, abstractmethod
from typing import List

# 违反 OCP:每新增一种通知方式都要修改 Notifier 类
class NotifierBad:
    def notify(self, message: str, method: str) -> None:
        if method == "email":
            print(f"[Email] {message}")
        elif method == "sms":
            print(f"[SMS] {message}")
        elif method == "wechat":
            print(f"[WeChat] {message}")
        # 新增方式?继续加 elif ... 违反 OCP


# 遵循 OCP:通过抽象基类定义接口,新增通知方式只需新增类
class NotificationChannel(ABC):
    @abstractmethod
    def send(self, message: str) -> None:
        pass

class EmailChannel(NotificationChannel):
    def send(self, message: str) -> None:
        print(f"[Email] {message}")

class SmsChannel(NotificationChannel):
    def send(self, message: str) -> None:
        print(f"[SMS] {message}")

class WeChatChannel(NotificationChannel):
    def send(self, message: str) -> None:
        print(f"[WeChat] {message}")

class Notifier:
    """通知器:对扩展开放,对修改封闭"""
    def __init__(self, channels: List[NotificationChannel]):
        self.channels = channels

    def notify_all(self, message: str) -> None:
        for channel in self.channels:
            channel.send(message)

# 新增 Slack 通知只需要新建一个类,不用改 Notifier
class SlackChannel(NotificationChannel):
    def send(self, message: str) -> None:
        print(f"[Slack] {message}")

notifier = Notifier([EmailChannel(), SmsChannel(), SlackChannel()])
notifier.notify_all("服务器告警: CPU 使用率 95%")
# [Email] 服务器告警: CPU 使用率 95%
# [SMS] 服务器告警: CPU 使用率 95%
# [Slack] 服务器告警: CPU 使用率 95%

3.3 里氏替换原则(LSP)

子类必须能够替换父类,而不影响程序的正确性。

python 复制代码
# 违反 LSP 的经典案例:正方形继承矩形
class Rectangle:
    def __init__(self, width: float, height: float):
        self._width = width
        self._height = height

    @property
    def width(self) -> float:
        return self._width

    @width.setter
    def width(self, value: float) -> None:
        self._width = value

    @property
    def height(self) -> float:
        return self._height

    @height.setter
    def height(self, value: float) -> None:
        self._height = value

    def area(self) -> float:
        return self._width * self._height

class Square(Rectangle):
    """正方形'是'矩形?从数学角度看是,但从 LSP 角度看不是"""
    def __init__(self, side: float):
        super().__init__(side, side)

    @Rectangle.width.setter
    def width(self, value: float) -> None:
        self._width = value
        self._height = value  # 正方形要求宽高相等

    @Rectangle.height.setter
    def height(self, value: float) -> None:
        self._width = value
        self._height = value

def resize_and_check(rect: Rectangle) -> None:
    """这个函数假设设置 width 不会影响 height"""
    rect.width = 10
    rect.height = 5
    expected = 50  # 10 * 5
    actual = rect.area()
    print(f"期望面积: {expected}, 实际面积: {actual}, "
          f"{'一致' if expected == actual else '不一致!'}")

resize_and_check(Rectangle(1, 1))  # 期望面积: 50, 实际面积: 50, 一致
resize_and_check(Square(1))        # 期望面积: 50, 实际面积: 25, 不一致!

遵循 LSP 的改写:使用共同的抽象基类,而不是让正方形继承矩形:

python 复制代码
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self) -> float:
        pass

class RectangleGood(Shape):
    def __init__(self, width: float, height: float):
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

class SquareGood(Shape):
    def __init__(self, side: float):
        self.side = side

    def area(self) -> float:
        return self.side ** 2

# 两者都是 Shape,但互不继承
print(RectangleGood(10, 5).area())  # 50
print(SquareGood(5).area())          # 25

3.4 接口隔离原则(ISP)

客户端不应该被迫依赖它不使用的方法。

在 Python 中,用 Protocol 或小粒度的 ABC 来实践 ISP:

python 复制代码
from typing import Protocol

# 违反 ISP:一个臃肿的接口
class WorkerBad(Protocol):
    def work(self) -> None: ...
    def eat(self) -> None: ...
    def sleep(self) -> None: ...

# 问题:机器人也是 Worker,但它不需要 eat 和 sleep


# 遵循 ISP:拆分为细粒度的 Protocol
class Workable(Protocol):
    def work(self) -> None: ...

class Eatable(Protocol):
    def eat(self) -> None: ...

class Sleepable(Protocol):
    def sleep(self) -> None: ...

class Human:
    """人类实现所有接口"""
    def work(self) -> None:
        print("人在工作")

    def eat(self) -> None:
        print("人在吃饭")

    def sleep(self) -> None:
        print("人在睡觉")

class Robot:
    """机器人只需要实现 Workable"""
    def work(self) -> None:
        print("机器人在工作")

def assign_work(worker: Workable) -> None:
    """只依赖 Workable 接口"""
    worker.work()

def lunch_break(entity: Eatable) -> None:
    """只依赖 Eatable 接口"""
    entity.eat()

assign_work(Human())  # 人在工作
assign_work(Robot())  # 机器人在工作
lunch_break(Human())  # 人在吃饭
# lunch_break(Robot())  # mypy 报错:Robot 没有 eat 方法

3.5 依赖倒置原则(DIP)

高层模块不应该依赖低层模块,两者都应该依赖抽象。

python 复制代码
from abc import ABC, abstractmethod
from typing import List, Dict, Any

# 违反 DIP:Service 直接依赖具体的 MySQL 实现
class MySQLDatabaseBad:
    def query(self, sql: str) -> List[Dict]:
        return [{"id": 1, "name": "Alice"}]

class UserServiceBad:
    def __init__(self):
        self.db = MySQLDatabaseBad()  # 直接依赖具体实现,无法替换

    def get_users(self) -> List[Dict]:
        return self.db.query("SELECT * FROM users")


# 遵循 DIP:依赖抽象接口,通过构造函数注入
class Database(ABC):
    @abstractmethod
    def query(self, sql: str) -> List[Dict[str, Any]]:
        pass

class MySQLDatabase(Database):
    def query(self, sql: str) -> List[Dict[str, Any]]:
        print(f"[MySQL] 执行: {sql}")
        return [{"id": 1, "name": "Alice"}]

class PostgreSQLDatabase(Database):
    def query(self, sql: str) -> List[Dict[str, Any]]:
        print(f"[PostgreSQL] 执行: {sql}")
        return [{"id": 1, "name": "Alice"}]

class InMemoryDatabase(Database):
    """用于单元测试的内存数据库"""
    def __init__(self):
        self._data: List[Dict[str, Any]] = []

    def query(self, sql: str) -> List[Dict[str, Any]]:
        return self._data

class UserService:
    def __init__(self, db: Database):  # 依赖抽象,通过构造函数注入
        self.db = db

    def get_users(self) -> List[Dict[str, Any]]:
        return self.db.query("SELECT * FROM users")

# 生产环境使用 MySQL
service = UserService(MySQLDatabase())
print(service.get_users())
# [MySQL] 执行: SELECT * FROM users
# [{'id': 1, 'name': 'Alice'}]

# 测试环境使用内存数据库
test_db = InMemoryDatabase()
test_db._data = [{"id": 99, "name": "TestUser"}]
test_service = UserService(test_db)
print(test_service.get_users())
# [{'id': 99, 'name': 'TestUser'}]

四、组合优于继承

4.1 继承滥用的反模式

继承应该表示 "is-a" 关系,但很多时候开发者为了代码复用而滥用继承,导致"继承地狱":

python 复制代码
# 反模式:为了复用代码而滥用继承
class Engine:
    def start(self) -> str:
        return "引擎启动"

class CarBad(Engine):
    """Car 不是 Engine!Car 有 Engine"""
    def drive(self) -> str:
        return f"{self.start()} -> 开始行驶"

# 更糟糕的继承链
class ElectricEngineMixin:
    def charge(self) -> str:
        return "充电中"

class GPSMixin:
    def navigate(self) -> str:
        return "导航中"

class LoggerMixin:
    def log(self, msg: str) -> str:
        return f"[LOG] {msg}"

class SuperCarBad(CarBad, ElectricEngineMixin, GPSMixin, LoggerMixin):
    """5 层继承 + 多重继承 = 维护噩梦"""
    pass

4.2 组合模式

使用组合替代继承 -- 表达 "has-a" 关系:

python 复制代码
class Engine:
    def start(self) -> str:
        return "引擎启动"

class GPS:
    def navigate(self, destination: str) -> str:
        return f"导航到 {destination}"

class Logger:
    def __init__(self, name: str):
        self.name = name

    def log(self, message: str) -> str:
        return f"[{self.name}] {message}"

class Car:
    """使用组合:Car 拥有 Engine、GPS、Logger"""
    def __init__(self, model: str):
        self.model = model
        self.engine = Engine()      # has-a
        self.gps = GPS()            # has-a
        self.logger = Logger(model) # has-a

    def drive(self, destination: str) -> None:
        print(self.logger.log(self.engine.start()))
        print(self.logger.log(self.gps.navigate(destination)))
        print(self.logger.log("行驶中..."))

car = Car("Tesla Model 3")
car.drive("公司")
# [Tesla Model 3] 引擎启动
# [Tesla Model 3] 导航到 公司
# [Tesla Model 3] 行驶中...

选择依据

关系类型 用法 例子
is-a(是) 继承 Dog is an Animal
has-a(有) 组合 Car has an Engine
behaves-like(行为像) Protocol/Mixin Logger behaves like Writable

4.3 Mixin 模式

Mixin 是一种特殊的多重继承用法:提供可复用的功能片段,但不构成独立的抽象。Mixin 类的特点是:

  1. 不应该被单独实例化
  2. 不应该有 __init__(或者只有极简的 __init__ 且使用 **kwargs
  3. 只提供特定的方法或属性
python 复制代码
import json
from typing import Dict, Any

class JsonSerializableMixin:
    """Mixin: 提供 JSON 序列化能力"""
    def to_json(self) -> str:
        return json.dumps(self._get_serializable_data(), ensure_ascii=False)

    def _get_serializable_data(self) -> Dict[str, Any]:
        """默认序列化所有非下划线开头的属性"""
        return {
            k: v for k, v in self.__dict__.items()
            if not k.startswith('_')
        }

class TimestampMixin:
    """Mixin: 提供时间戳功能"""
    def __init_subclass__(cls, **kwargs):
        super().__init_subclass__(**kwargs)
        # 自动为子类添加时间戳
        original_init = cls.__init__ if hasattr(cls, '__init__') else None

    def set_timestamps(self) -> None:
        import time
        now = time.time()
        if not hasattr(self, 'created_at'):
            self.created_at = now
        self.updated_at = now

class ReprMixin:
    """Mixin: 自动生成 __repr__"""
    def __repr__(self) -> str:
        attrs = ", ".join(
            f"{k}={v!r}"
            for k, v in self.__dict__.items()
            if not k.startswith('_')
        )
        return f"{self.__class__.__name__}({attrs})"

# 组合多个 Mixin
class User(JsonSerializableMixin, ReprMixin):
    def __init__(self, name: str, email: str, age: int):
        self.name = name
        self.email = email
        self.age = age

user = User("Alice", "alice@example.com", 30)
print(repr(user))
# User(name='Alice', email='alice@example.com', age=30)

print(user.to_json())
# {"name": "Alice", "email": "alice@example.com", "age": 30}

Mixin 的命名规范 :类名以 Mixin 结尾,如 JsonSerializableMixinLoggingMixinCacheMixin。这是 Python 社区的普遍约定。

4.4 继承 vs 组合 vs Mixin 性能对比

python 复制代码
import timeit

# 方式1:深继承链
class Base1:
    def method(self) -> int:
        return 42

class Level1(Base1): pass
class Level2(Level1): pass
class Level3(Level2): pass
class Level4(Level3): pass
class DeepChild(Level4): pass

# 方式2:直接实现
class DirectClass:
    def method(self) -> int:
        return 42

# 方式3:组合
class Component:
    def method(self) -> int:
        return 42

class ComposedClass:
    def __init__(self):
        self._component = Component()

    def method(self) -> int:
        return self._component.method()

deep = DeepChild()
direct = DirectClass()
composed = ComposedClass()

# 性能对比
t_deep = timeit.timeit(lambda: deep.method(), number=1000000)
t_direct = timeit.timeit(lambda: direct.method(), number=1000000)
t_composed = timeit.timeit(lambda: composed.method(), number=1000000)

print(f"深继承链:  {t_deep:.4f}s")
print(f"直接实现:  {t_direct:.4f}s")
print(f"组合委托:  {t_composed:.4f}s")

典型结果:

复制代码
深继承链:  0.0850s
直接实现:  0.0750s
组合委托:  0.1100s

分析 :深继承链查找方法时需要遍历 MRO 链,但 CPython 有方法缓存机制,性能差距不大。组合方式因为多了一次属性访问和函数调用,略慢一些。性能差距在毫秒级别,设计可维护性远比这点性能差异重要


五、魔法方法(Dunder Methods)大全

魔法方法(双下划线方法,Dunder Methods)是 Python 数据模型的核心。通过实现这些方法,自定义类可以完美融入 Python 的语法和内置函数体系。

5.1 对象表示:__repr____str__

python 复制代码
class Money:
    def __init__(self, amount: float, currency: str = "CNY"):
        self.amount = amount
        self.currency = currency

    def __repr__(self) -> str:
        """开发者看的:无歧义,可用于重建对象"""
        return f"Money(amount={self.amount!r}, currency={self.currency!r})"

    def __str__(self) -> str:
        """用户看的:可读性优先"""
        symbols = {"CNY": "¥", "USD": "$", "EUR": "€"}
        symbol = symbols.get(self.currency, self.currency)
        return f"{symbol}{self.amount:,.2f}"

    def __format__(self, format_spec: str) -> str:
        """支持 format() 和 f-string 的格式化"""
        if format_spec == "short":
            return f"{self.currency} {self.amount:.0f}"
        return str(self)

m = Money(12345.6, "CNY")
print(repr(m))         # Money(amount=12345.6, currency='CNY')
print(str(m))          # ¥12,345.60
print(f"{m}")          # ¥12,345.60
print(f"{m:short}")    # CNY 12346

# repr 的黄金准则:理想情况下 eval(repr(obj)) == obj
m2 = eval(repr(m))
print(m2.amount)       # 12345.6

__repr__ vs __str__ 的调用时机

场景 调用方法
repr(obj) __repr__
str(obj) __str__(没有则回退到 __repr__
print(obj) __str__
f"{obj}" __str__
交互式解释器直接输入 obj __repr__
容器中显示(如 [obj] __repr__

原则:每个类都应该实现 __repr__,可选实现 __str__

5.2 比较运算:__eq____lt____hash__

python 复制代码
from functools import total_ordering

@total_ordering  # 只需定义 __eq__ 和 __lt__,自动生成其余比较方法
class Version:
    """语义化版本号"""
    def __init__(self, major: int, minor: int, patch: int):
        self.major = major
        self.minor = minor
        self.patch = patch

    def __repr__(self) -> str:
        return f"Version({self.major}, {self.minor}, {self.patch})"

    def __str__(self) -> str:
        return f"v{self.major}.{self.minor}.{self.patch}"

    def __eq__(self, other: object) -> bool:
        if not isinstance(other, Version):
            return NotImplemented  # 让 Python 尝试反向操作
        return (self.major, self.minor, self.patch) == \
               (other.major, other.minor, other.patch)

    def __lt__(self, other: object) -> bool:
        if not isinstance(other, Version):
            return NotImplemented
        return (self.major, self.minor, self.patch) < \
               (other.major, other.minor, other.patch)

    def __hash__(self) -> int:
        """定义了 __eq__ 就必须定义 __hash__(否则变为不可哈希)"""
        return hash((self.major, self.minor, self.patch))

v1 = Version(1, 2, 3)
v2 = Version(1, 3, 0)
v3 = Version(1, 2, 3)

print(v1 < v2)     # True
print(v1 == v3)     # True
print(v1 >= v2)     # False -- total_ordering 自动生成
print(v1 != v2)     # True
print(sorted([v2, v1, Version(2, 0, 0)]))
# [Version(1, 2, 3), Version(1, 3, 0), Version(2, 0, 0)]

# 可以用作字典键和集合元素(因为实现了 __hash__)
version_features = {v1: ["feature_a"], v2: ["feature_b"]}
print(version_features[Version(1, 2, 3)])  # ['feature_a']

__eq____hash__ 的关系

  • 如果定义了 __eq__ 但没定义 __hash__,Python 会把 __hash__ 设为 None,对象变为不可哈希
  • 不可哈希的对象不能作为 dict 的键或 set 的元素
  • 规则:相等的对象必须有相同的哈希值a == bhash(a) == hash(b)

5.3 容器协议:__len____getitem____contains__

实现容器协议可以让自定义类像 list/dict 一样使用:

python 复制代码
from typing import Optional, Iterator

class Playlist:
    """实现序列协议的播放列表"""

    def __init__(self, name: str):
        self.name = name
        self._songs: list = []

    def add(self, song: str) -> None:
        self._songs.append(song)

    # 序列协议
    def __len__(self) -> int:
        """支持 len(playlist)"""
        return len(self._songs)

    def __getitem__(self, index):
        """支持索引和切片:playlist[0], playlist[1:3]"""
        if isinstance(index, slice):
            result = Playlist(f"{self.name}[slice]")
            result._songs = self._songs[index]
            return result
        return self._songs[index]

    def __setitem__(self, index: int, value: str) -> None:
        """支持赋值:playlist[0] = 'new_song'"""
        self._songs[index] = value

    def __delitem__(self, index: int) -> None:
        """支持删除:del playlist[0]"""
        del self._songs[index]

    def __contains__(self, item: str) -> bool:
        """支持 in 运算符:'song' in playlist"""
        return item in self._songs

    def __iter__(self) -> Iterator[str]:
        """支持 for 循环:for song in playlist"""
        return iter(self._songs)

    def __reversed__(self) -> Iterator[str]:
        """支持 reversed(playlist)"""
        return reversed(self._songs)

    def __repr__(self) -> str:
        return f"Playlist(name={self.name!r}, songs={self._songs!r})"

# 使用
playlist = Playlist("我的歌单")
playlist.add("晴天")
playlist.add("夜曲")
playlist.add("七里香")
playlist.add("稻香")

print(len(playlist))             # 4
print(playlist[0])               # 晴天
print(playlist[1:3])             # Playlist(name='我的歌单[slice]', songs=['夜曲', '七里香'])
print("夜曲" in playlist)        # True
print("双截棍" in playlist)      # False

for song in playlist:
    print(f"  播放: {song}")

# 支持 reversed
for song in reversed(playlist):
    print(f"  倒放: {song}")

playlist[0] = "简单爱"
print(playlist[0])               # 简单爱

del playlist[0]
print(len(playlist))             # 3

5.4 上下文管理:__enter____exit__

python 复制代码
import time
from typing import Optional, Type
from types import TracebackType

class Timer:
    """计时器上下文管理器"""

    def __init__(self, label: str = "代码块"):
        self.label = label
        self.elapsed: float = 0.0

    def __enter__(self) -> "Timer":
        self._start = time.perf_counter()
        return self  # 作为 as 变量的值

    def __exit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType]
    ) -> bool:
        self.elapsed = time.perf_counter() - self._start
        print(f"[{self.label}] 耗时: {self.elapsed:.4f}s")
        # 返回 False 表示不吞掉异常,返回 True 则吞掉
        return False

# 使用
with Timer("列表推导式") as t:
    data = [i ** 2 for i in range(100000)]
print(f"记录的耗时: {t.elapsed:.4f}s")

# 异常处理示例
class SuppressError:
    """吞掉指定类型的异常"""
    def __init__(self, *exceptions: Type[Exception]):
        self.exceptions = exceptions

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> bool:
        if exc_type is not None and issubclass(exc_type, self.exceptions):
            print(f"已吞掉异常: {exc_type.__name__}: {exc_val}")
            return True  # 吞掉异常
        return False

with SuppressError(ZeroDivisionError, ValueError):
    result = 1 / 0  # 不会抛出异常
print("程序继续运行")  # 正常执行
# 已吞掉异常: ZeroDivisionError: division by zero
# 程序继续运行

5.5 数值运算:__add____radd____iadd__

python 复制代码
class Vector:
    """二维向量,演示完整的数值运算协议"""

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

    def __repr__(self) -> str:
        return f"Vector({self.x}, {self.y})"

    # ---------- 算术运算 ----------
    def __add__(self, other):
        """v1 + v2"""
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        if isinstance(other, (int, float)):
            return Vector(self.x + other, self.y + other)
        return NotImplemented

    def __radd__(self, other):
        """当左操作数不支持时调用:5 + v"""
        return self.__add__(other)

    def __iadd__(self, other):
        """v1 += v2(就地加法)"""
        if isinstance(other, Vector):
            self.x += other.x
            self.y += other.y
            return self
        if isinstance(other, (int, float)):
            self.x += other
            self.y += other
            return self
        return NotImplemented

    def __mul__(self, scalar):
        """v * 3(标量乘法)"""
        if isinstance(scalar, (int, float)):
            return Vector(self.x * scalar, self.y * scalar)
        return NotImplemented

    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 __bool__(self) -> bool:
        """bool(v):零向量为 False"""
        return self.x != 0 or self.y != 0

v1 = Vector(3, 4)
v2 = Vector(1, 2)

print(v1 + v2)        # Vector(4, 6)
print(v1 + 10)        # Vector(13, 14)
print(10 + v1)        # Vector(13, 14) -- 触发 __radd__
print(v1 * 3)         # Vector(9, 12)
print(3 * v1)         # Vector(9, 12) -- 触发 __rmul__
print(-v1)            # Vector(-3, -4)
print(abs(v1))        # 5.0 -- 勾股定理
print(bool(v1))       # True
print(bool(Vector(0, 0)))  # False

v3 = Vector(1, 1)
v3 += v2              # 触发 __iadd__
print(v3)             # Vector(2, 3)

__add__ vs __radd__ vs __iadd__ 调用时机

复制代码
v1 + v2     → v1.__add__(v2)
              如果返回 NotImplemented → v2.__radd__(v1)

5 + v1      → (5).__add__(v1) → NotImplemented
              → v1.__radd__(5)

v1 += v2    → v1.__iadd__(v2)
              如果没有 __iadd__ → v1 = v1.__add__(v2)

5.6 魔法方法速查表

类别 方法 触发方式 用途
构造 __new__ MyClass() 创建实例
__init__ MyClass() 初始化实例
__del__ 垃圾回收时 析构(少用)
表示 __repr__ repr(obj) 开发者表示
__str__ str(obj), print(obj) 用户表示
__format__ format(obj), f"{obj}" 格式化
__bytes__ bytes(obj) 字节表示
比较 __eq__ / __ne__ == / != 相等判断
__lt__ / __le__ < / <= 大小比较
__gt__ / __ge__ > / >= 大小比较
__hash__ hash(obj) 哈希值
容器 __len__ len(obj) 长度
__getitem__ obj[key] 索引/键访问
__setitem__ obj[key] = val 索引/键赋值
__delitem__ del obj[key] 索引/键删除
__contains__ item in obj 成员检测
__iter__ for x in obj 迭代
__next__ next(obj) 迭代器
__reversed__ reversed(obj) 反向迭代
数值 __add__ / __radd__ a + b / b + a 加法
__sub__ / __rsub__ a - b 减法
__mul__ / __rmul__ a * b 乘法
__truediv__ a / b 除法
__floordiv__ a // b 整除
__mod__ a % b 取模
__pow__ a ** b 幂运算
__neg__ / __pos__ -a / +a 一元运算
__abs__ abs(a) 绝对值
上下文 __enter__ with obj as x 进入
__exit__ with 块结束时 退出
属性 __getattr__ obj.missing_attr 属性未找到时
__getattribute__ obj.any_attr 所有属性访问
__setattr__ obj.attr = val 属性赋值
__delattr__ del obj.attr 属性删除
可调用 __call__ obj() 实例作为函数
布尔 __bool__ bool(obj), if obj 真值判断

六、面试高频题详解

Q1:__new____init__ 的区别是什么?

python 复制代码
# 一句话:__new__ 创建对象,__init__ 初始化对象

# __new__ 是类方法(第一个参数 cls),负责分配内存并返回实例
# __init__ 是实例方法(第一个参数 self),负责设置属性

# 关键点:
# 1. __new__ 先于 __init__ 执行
# 2. __new__ 必须返回一个实例,否则 __init__ 不会被调用
# 3. 通常只在以下场景重写 __new__:
#    - 实现单例模式
#    - 继承不可变类型(int, str, tuple)
#    - 控制实例创建过程(如对象池)

# 单例模式面试手写
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, name: str = "default"):
        self.name = name

s1 = Singleton("first")
s2 = Singleton("second")
print(s1 is s2)    # True
print(s1.name)     # second -- 注意 __init__ 每次都会执行

Q2:解释 Python 的 MRO 机制

python 复制代码
# MRO(Method Resolution Order)是 Python 在多重继承场景下
# 确定方法查找顺序的机制,使用 C3 线性化算法。

# 核心规则:
# 1. 子类在父类之前
# 2. 声明顺序决定优先级(class D(B, C):B 优先于 C)
# 3. 公共基类在所有子类之后

class A:
    def who(self) -> str:
        return "A"

class B(A):
    def who(self) -> str:
        return "B"

class C(A):
    def who(self) -> str:
        return "C"

class D(B, C):
    pass  # 不重写 who

# MRO: D -> B -> C -> A -> object
print(D().who())     # B
print(D.__mro__)

# super() 遵循 MRO 链,不是简单调用"父类"
class E(B, C):
    def who(self) -> str:
        # super() 调用的是 MRO 链中的下一个,即 B
        return f"E -> {super().who()}"

print(E().who())  # E -> B

Q3:什么是鸭子类型?与显式接口有什么区别?

python 复制代码
# 鸭子类型:不关心对象的类型,只关心它是否有所需的方法/属性
# "If it quacks like a duck, it is a duck"

# 鸭子类型:任何有 read 方法的对象都可以传入
def read_data(source):
    return source.read()

# 无需继承任何基类,只要有 read 方法即可
class FileSource:
    def read(self) -> str:
        return "文件数据"

class NetworkSource:
    def read(self) -> str:
        return "网络数据"

class StringSource:
    def read(self) -> str:
        return "字符串数据"

for src in [FileSource(), NetworkSource(), StringSource()]:
    print(read_data(src))

# 对比显式接口(ABC):必须继承基类
from abc import ABC, abstractmethod

class DataSource(ABC):
    @abstractmethod
    def read(self) -> str:
        pass

class TypedFileSource(DataSource):
    def read(self) -> str:
        return "文件数据"

# 对比 Protocol(Python 3.8+):结构化子类型,兼顾两者优势
from typing import Protocol

class Readable(Protocol):
    def read(self) -> str: ...

def read_data_typed(source: Readable) -> str:
    """mypy 会检查传入对象是否有 read 方法,但不要求继承"""
    return source.read()
方式 优点 缺点
鸭子类型 灵活、无侵入 运行时才报错
ABC 强约束、文档性强 需要继承,侵入性
Protocol 兼顾灵活和类型安全 需要 Python 3.8+

Q4:组合和继承各自的适用场景是什么?

python 复制代码
# 继承:is-a 关系
# - 子类确实"是"父类的一种特化
# - 需要利用多态(如框架要求继承特定基类)
# - 子类需要修改父类的行为(方法重写)

# 典型正确使用继承的场景
from abc import ABC, abstractmethod

class Serializer(ABC):
    @abstractmethod
    def serialize(self, data: dict) -> str:
        pass

class JsonSerializer(Serializer):  # JsonSerializer IS-A Serializer
    def serialize(self, data: dict) -> str:
        import json
        return json.dumps(data)

class XmlSerializer(Serializer):   # XmlSerializer IS-A Serializer
    def serialize(self, data: dict) -> str:
        items = "".join(f"<{k}>{v}</{k}>" for k, v in data.items())
        return f"<root>{items}</root>"

# 组合:has-a 关系
# - 功能复用但不存在"是一种"关系
# - 需要更灵活地替换组件
# - 避免深继承链

class Logger:
    def log(self, msg: str) -> None:
        print(f"[LOG] {msg}")

class Cache:
    def __init__(self):
        self._store: dict = {}

    def get(self, key: str):
        return self._store.get(key)

    def set(self, key: str, value) -> None:
        self._store[key] = value

class ApiService:
    """ApiService HAS-A Logger and HAS-A Cache"""
    def __init__(self, logger: Logger, cache: Cache):
        self.logger = logger
        self.cache = cache

    def get_data(self, key: str) -> str:
        cached = self.cache.get(key)
        if cached:
            self.logger.log(f"缓存命中: {key}")
            return cached
        self.logger.log(f"缓存未命中: {key}, 从数据库读取")
        value = f"data_for_{key}"
        self.cache.set(key, value)
        return value

service = ApiService(Logger(), Cache())
print(service.get_data("user_1"))
print(service.get_data("user_1"))
# [LOG] 缓存未命中: user_1, 从数据库读取
# data_for_user_1
# [LOG] 缓存命中: user_1
# data_for_user_1

本章总结

本文从底层机制到设计原则,系统性地剖析了 Python 面向对象编程的高级特性:

  1. 类的两阶段构造__new__ 负责创建实例(分配内存),__init__ 负责初始化属性。重写 __new__ 的典型场景是单例模式和不可变类型扩展。

  2. MRO 与 super() :Python 使用 C3 线性化算法确定多重继承的方法查找顺序。super() 不是"调用父类",而是调用 MRO 链中的下一个类,这是协作式多重继承的基础。

  3. Python 的封装与多态 :名称改编(__name)防止子类意外覆盖,但不是安全机制。鸭子类型是 Python 多态的核心哲学,Protocol 提供了鸭子类型的静态检查能力。

  4. SOLID 原则:在 Python 中,SRP 通过职责拆分实现,OCP 通过抽象基类/Protocol 实现,LSP 要求子类完全替代父类,ISP 通过细粒度 Protocol 实现,DIP 通过构造函数注入实现。

  5. 组合优于继承:继承表示"is-a"关系,组合表示"has-a"关系。Mixin 是多重继承的特殊用法,提供可复用的功能片段。优先选择组合,只在真正的"is-a"场景使用继承。

  6. 魔法方法 :通过实现 __repr____eq____hash____len____getitem____enter__/__exit____add__ 等协议方法,自定义类可以无缝融入 Python 生态。

核心原则 :Python 的 OOP 不是 Java 式的"一切皆对象"强制范式,而是提供工具让你在需要时使用。简单场景用函数和字典,中等复杂度用 dataclass,复杂系统才用完整的 OOP 设计。不要为了 OOP 而 OOP


下一篇预告

第 04 篇:Python 内存管理与垃圾回收 -- 从引用计数到分代回收

下一篇文章将深入 Python 内存管理的底层世界。你将了解:

  • Python 对象的内存布局:PyObject 和 PyVarObject 的结构,小整数缓存池(-5~256),字符串驻留机制
  • 引用计数机制 :引用计数的增减规则,sys.getrefcount() 的使用,循环引用的问题
  • 分代垃圾回收:Generation 0/1/2 的分代策略,标记-清除算法如何解决循环引用
  • 内存优化实战__slots__ 的原理与限制,生成器 vs 列表的内存效率,tracemalloc 排查内存泄漏
  • CPython 内存分配器:pymalloc 小对象分配器,Arena/Pool/Block 三级结构,Python 进程"内存只增不减"的真相

理解内存管理机制不仅是面试热点,更是排查线上内存泄漏、优化大数据处理性能的必备技能。


Python 后端开发技术博客专栏 | 作者:耿雨飞

本文为专栏第 03 篇,共 25 篇。完整目录请参阅《Python技术博客专栏大纲》。

相关推荐
charlie1145141912 小时前
嵌入式现代C++工程实践——第14篇:第二次重构 —— 模板登场,编译时绑定端口和引脚
开发语言·c++·stm32·安全·重构
m0_678485452 小时前
c++如何提取系统环境变量并直接保存到txt日志中_getenv与ofstream【实战】
jvm·数据库·python
源码站~2 小时前
基于python的校园代跑(跑腿)系统
开发语言·python
BugShare2 小时前
一个用 Rust 编写的、速度极快的 Python 包和项目管理器
开发语言·python·rust
qq_342295822 小时前
Go语言怎么嵌入静态文件_Go语言embed嵌入文件教程【秒懂】
jvm·数据库·python
耿雨飞2 小时前
Python 后端开发技术博客专栏 | 第 04 篇 Python 内存管理与垃圾回收 -- 从引用计数到分代回收
开发语言·python·垃圾回收
雾岛听蓝2 小时前
Qt 输入与多元素控件详解
开发语言·经验分享·笔记·qt
qq_206901392 小时前
如何在Linux上源码编译安装MySQL_CMake配置与依赖包安装
jvm·数据库·python
执笔画流年呀2 小时前
多线程及其特性
java·服务器·开发语言