跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python算法

Python 面向对象进阶:封装、继承、多态与元类详解

深入讲解 Python 面向对象编程(OOP)的核心概念。涵盖类与对象的基础定义、内存布局、三大特性(封装、继承、多态)。详细解析方法解析顺序(MRO)、描述符机制及其与 property 的关系、属性查找流程。此外,还介绍了元类的原理与应用,以及魔法方法的使用。最后通过 RPG 战斗系统示例综合实践,并给出避免过度设计和合理使用继承的建议。

云间运维发布于 2026/3/27更新于 2026/9/163 浏览

1. 核心问题:为什么我们要搞 OOP?

面向对象编程(Object-Oriented Programming, OOP)不是为了把简单的事情搞复杂,而是为了在大规模协作时,代码不至于变成一团乱麻。

  • 过程式编程:像是在写一份详细的"炒菜步骤"。
  • 面向对象编程:像是在雇佣一个"专业厨师"。你不需要知道他怎么切菜、怎么控温,你只需要告诉他:"厨师,给我来份宫保鸡丁!"

本文将探讨如何从编写步骤转变为设计对象。


2. 基础:类(Class)与对象(Object)

2.1 模具与成品
  • 类 (Class):就是一张"图纸"或者一个"模具"。它定义了东西长啥样,能干啥。
  • 对象 (Object):就是根据图纸造出来的"实物"。
class Robot:
    def __init__(self, name, version):
        # self 就是对象自己,像是每个人的身份证
        self.name = name
        self.version = version

    def say_hello(self):
        print(f"你好!我是机器人 {self.name},版本号:{self.version}")

# 造两个机器人
r1 = Robot("小爱", "v1.0")
r2 = Robot("小度", "v2.0")
r1.say_hello()
r2.say_hello()
2.2 self 的作用

self 就像是函数里的一个占位符。当你喊 r1.say_hello() 时,Python 自动把 r1 这个实例传给了 self。这样函数才知道该打印"小爱"还是"小度"。

2.3 类与对象的内存布局

理解内存布局是掌握面向对象的关键。让我们看看 Python 中类和对象是如何在内存中存储的:

class Person:
    species = "Homo sapiens"  # 类属性(存在类对象中)

    def __init__(self, name, age):
        self.name = name  
        .age = age

     ():  
         


p1 = Person(, )
p2 = Person(, )


(Person.species)  
(p1.species)      
(p2.species)      


Person.species = 
(p1.species)      


p1.species = 
(p1.species)      
(p2.species)      
(Person.species)  
# 实例属性(存在实例对象中)
self
def
greet
self
# 实例方法
return
f"Hello, I'm {self.name}"
# 内存布局分析
"Alice"
25
"Bob"
30
# 类属性是所有实例共享的
print
# Homo sapiens
print
# Homo sapiens(通过实例访问类属性)
print
# Homo sapiens
# 修改类属性会影响所有实例
"Human"
print
# Human
# 但给实例赋值会创建实例属性,不影响其他实例
"Modified"
print
# Modified(实例属性遮蔽类属性)
print
# Human(仍然访问类属性)
print
# Human

内存布局要点:

  • 类对象:存储类属性、方法定义,只有一份
  • 实例对象:存储实例属性,每个实例独立一份
  • 方法查找:实例方法实际存储在类中,实例通过 __class__ 引用找到方法

3. 三大特性:封装、继承、多态

3.1 封装 (Encapsulation):把脏活累活藏起来

你不希望别人随便改你的内部数据。在 Python 里,用双下划线 __ 就能把变量藏起来。

class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # 私有变量,外面看不到

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print(f"存入成功,当前余额:{self.__balance}")

    def get_balance(self):
        return self.__balance

account = BankAccount(1000)
# print(account.__balance)  # 这行会报错!
print(account.get_balance())  # 只能通过我给你的接口来查
3.2 继承 (Inheritance):少写代码的捷径

如果你要写"猫"和"狗",它们都有"吃"和"睡"的功能,那就先写个"动物"类。

class Animal:
    def eat(self):
        print("正在吃东西...")

class Dog(Animal):  # 狗继承了动物
    def bark(self):
        print("汪汪汪!")

my_dog = Dog()
my_dog.eat()  # 继承来的
my_dog.bark()  # 自己有的
3.3 多态 (Polymorphism):鸭子类型

Python 的多态特别灵活。只要你长得像鸭子,叫起来也像鸭子,那我就把你当鸭子。


4. 深入:MRO(方法解析顺序)算法详解

当类存在多重继承时,Python 如何决定调用哪个方法?这就是 MRO(Method Resolution Order)要解决的问题。

4.1 什么是 MRO

MRO 定义了 Python 查找方法和属性的顺序。Python 3 使用 C3 线性化算法 来计算 MRO。

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

# 查看 MRO
print(D.__mro__)  # 输出:(<class 'D'>, <class 'B'>, <class 'C'>, <class 'A'>, <class 'object'>)
4.2 C3 线性化算法的核心规则
  1. 子类优先于父类:先查找子类,再查找父类
  2. 单调性:如果 C 是 D 的父类,那么在 C 的 MRO 中出现的类,在 D 的 MRO 中也以相同顺序出现
  3. 从左到右:按照继承列表中的顺序查找
# 复杂继承示例
class Base:
    def greet(self):
        print("Base greet")

class Mixin1(Base):
    def greet(self):
        print("Mixin1 greet")
        super().greet()

class Mixin2(Base):
    def greet(self):
        print("Mixin2 greet")
        super().greet()

class Combined(Mixin1, Mixin2):
    def greet(self):
        print("Combined greet")
        super().greet()

# MRO: Combined -> Mixin1 -> Mixin2 -> Base -> object
c = Combined()
c.greet()
# 输出:
# Combined greet
# Mixin1 greet
# Mixin2 greet
# Base greet
4.3 使用 super() 的正确姿势

super() 不是调用父类,而是按照 MRO 顺序调用下一个类。

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 -> B -> C -> A
# 注意:A 的 super() 调用 object.__init__(),什么都不做

5. 进阶:描述符(Descriptor)机制

描述符是 Python 中实现属性访问控制的核心机制。理解描述符,你就理解了 @property、@staticmethod、@classmethod 的本质。

5.1 什么是描述符

描述符协议:实现了 __get__、__set__ 或 __delete__ 方法的类就是描述符。

class Validator:
    """一个描述符,用于验证属性值"""
    def __init__(self, min_value, max_value):
        self.min_value = min_value
        self.max_value = max_value
        self.name = None

    def __set_name__(self, owner, name):
        """当描述符被赋值给类属性时调用"""
        self.name = name
        self.storage_name = f"_{name}"

    def __get__(self, instance, owner):
        """获取属性值时调用"""
        if instance is None:
            return self
        return getattr(instance, self.storage_name, None)

    def __set__(self, instance, value):
        """设置属性值时调用"""
        if not isinstance(value, (int, float)):
            raise TypeError(f"{self.name} must be a number")
        if not self.min_value <= value <= self.max_value:
            raise ValueError(f"{self.name} must be between {self.min_value} and {self.max_value}")
        setattr(instance, self.storage_name, value)

class Person:
    age = Validator(0, 150)  # age 是一个描述符实例
    salary = Validator(0, 1000000)

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age  # 触发描述符的 __set__
        self.salary = salary  # 同上

p = Person("Alice", 25, 50000)
print(p.age)  # 25
# p.age = 200  # ValueError: age must be between 0 and 150
# p.salary = "high"  # TypeError: salary must be a number
5.2 描述符的类型
类型实现的方法说明
非数据描述符只有 __get__实例属性优先于描述符
数据描述符有 __set__ 或 __delete__描述符优先于实例属性
class NonDataDescriptor:
    """非数据描述符"""
    def __get__(self, instance, owner):
        return "from descriptor"

class DataDescriptor:
    """数据描述符"""
    def __get__(self, instance, owner):
        return "from descriptor"

    def __set__(self, instance, value):
        print("Setting value")

class TestNonData:
    attr = NonDataDescriptor()

class TestData:
    attr = DataDescriptor()

# 非数据描述符:实例属性优先
t1 = TestNonData()
t1.attr = "from instance"
print(t1.attr)  # from instance

# 数据描述符:描述符优先
t2 = TestData()
t2.attr = "from instance"  # 触发 __set__
print(t2.attr)  # from descriptor(__get__ 仍然被调用)
5.3 property 的本质

@property 其实就是用描述符实现的:

# property 的简化版实现
class Property:
    def __init__(self, fget=None, fset=None, fdel=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel

    def __get__(self, instance, owner):
        if instance is None:
            return self
        if self.fget is None:
            raise AttributeError("can't get attribute")
        return self.fget(instance)

    def __set__(self, instance, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(instance, value)

    def setter(self, fset):
        self.fset = fset
        return self

# 使用自定义 Property
class Circle:
    def __init__(self, radius):
        self._radius = radius

    @Property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

6. 进阶:属性查找机制

理解 Python 如何查找属性,是掌握面向对象编程的关键。

6.1 属性查找顺序

对于 obj.attr,Python 按以下顺序查找:

  1. 数据描述符:在 obj.__class__ 中查找 attr,如果是数据描述符,返回描述符的值
  2. 实例字典:在 obj.__dict__ 中查找 attr
  3. 非数据描述符:在 obj.__class__ 及其父类的 __dict__ 中查找 attr
  4. 类属性:在 obj.__class__ 及其父类的 __dict__ 中查找 attr
  5. __getattr__:如果以上都没找到,调用 __getattr__
class Descriptor:
    """数据描述符"""
    def __get__(self, instance, owner):
        return "descriptor value"

    def __set__(self, instance, value):
        pass

class MyClass:
    attr = Descriptor()

    def __init__(self):
        self.__dict__['attr'] = "instance value"

    def __getattr__(self, name):
        return f"{name} not found, using getattr"

obj = MyClass()
print(obj.attr)  # descriptor value(数据描述符优先于实例属性)

# 如果描述符没有 __set__
class NonDataDescriptor:
    def __get__(self, instance, owner):
        return "descriptor value"

class MyClass2:
    attr = NonDataDescriptor()

    def __init__(self):
        self.attr = "instance value"

obj2 = MyClass2()
print(obj2.attr)  # instance value(实例属性优先于非数据描述符)
6.2 自定义属性访问
class AttributeTracer:
    """追踪所有属性访问"""
    def __init__(self):
        self._data = {}

    def __getattribute__(self, name):
        """拦截所有属性访问"""
        if name.startswith('_'):
            return object.__getattribute__(self, name)
        print(f"Getting: {name}")
        return object.__getattribute__(self, '_data').get(name)

    def __setattr__(self, name, value):
        """拦截所有属性设置"""
        if name.startswith('_'):
            object.__setattr__(self, name, value)
        else:
            print(f"Setting: {name} = {value}")
            object.__getattribute__(self, '_data')[name] = value

    def __getattr__(self, name):
        """属性不存在时调用"""
        print(f"Attribute {name} not found")
        return None

obj = AttributeTracer()
obj.x = 10  # Setting: x = 10
print(obj.x)  # Getting: x -> 10
print(obj.y)  # Getting: y -> Attribute y not found -> None

7. 进阶:元类(Metaclass)入门

元类是"类的类",它控制类的创建过程。理解元类,你就站在了 Python 面向对象编程的顶端。

7.1 什么是元类

在 Python 中,一切皆对象。类也是对象,而创建类的"东西"就是元类。默认情况下,所有类都是由 type 创建的。

class MyClass:
    pass

# MyClass 是 type 的实例
print(type(MyClass))  # <class 'type'>

# 等价于 MyClass2 = type('MyClass2', (), {})
MyClass2 = type('MyClass2', (), {})
print(type(MyClass2))  # <class 'type'>
7.2 自定义元类

通过继承 type,我们可以控制类的创建过程:

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, connection_string):
        self.connection_string = connection_string
        print(f"Initializing database with {connection_string}")

db1 = Database("postgresql://localhost/db")
db2 = Database("mysql://localhost/db")
print(db1 is db2)  # True(同一个实例)
print(db1.connection_string)  # postgresql://localhost/db
7.3 元类的 __new__ 和 __init__
class AutoRegisterMeta(type):
    """自动注册子类的元类"""
    registry = {}

    def __new__(mcs, name, bases, namespace):
        """创建类对象时调用"""
        print(f"Creating class: {name}")
        # 可以修改类的属性和方法
        namespace['created_by'] = 'AutoRegisterMeta'
        cls = super().__new__(mcs, name, bases, namespace)
        # 注册类(排除元类本身)
        if name != 'BaseModel':
            mcs.registry[name] = cls
        return cls

    def __init__(cls, name, bases, namespace):
        """初始化类对象时调用"""
        print(f"Initializing class: {name}")
        super().__init__(name, bases, namespace)

class BaseModel(metaclass=AutoRegisterMeta):
    """所有模型的基类"""
    pass

class User(BaseModel):
    pass

class Product(BaseModel):
    pass

print(AutoRegisterMeta.registry)  # {'User': <class '__main__.User'>, 'Product': <class '__main__.Product'>}
7.4 元类的实际应用
class ORMMeta(type):
    """简化 ORM 模型定义的元类"""
    def __new__(mcs, name, bases, namespace):
        # 收集所有字段定义
        fields = {}
        for key, value in list(namespace.items()):
            if isinstance(value, Field):
                fields[key] = value
                value.name = key
        namespace['_fields'] = fields
        return super().__new__(mcs, name, bases, namespace)

class Field:
    """字段定义"""
    def __init__(self, field_type, nullable=True):
        self.field_type = field_type
        self.nullable = nullable
        self.name = None

    def __repr__(self):
        return f"Field({self.field_type})"

class Model(metaclass=ORMMeta):
    """ORM 基类"""
    def __init__(self, **kwargs):
        for name, field in self._fields.items():
            value = kwargs.get(name)
            if value is None and not field.nullable:
                raise ValueError(f"{name} is required")
            setattr(self, name, value)

    def save(self):
        """模拟保存到数据库"""
        data = {name: getattr(self, name) for name in self._fields}
        print(f"Saving {self.__class__.__name__}: {data}")

class User(Model):
    id = Field(int, nullable=False)
    name = Field(str, nullable=False)
    email = Field(str)

user = User(id=1, name="Alice", email="[email protected]")
user.save()  # Saving User: {'id': 1, 'name': 'Alice', 'email': '[email protected]'}

8. 魔法方法:让你的类更"Pythonic"

Python 里有很多双下划线开头结尾的方法(Dunder Methods),它们能让你的对象支持 +, -, len() 等操作。

class Book:
    def __init__(self, title, pages):
        self.title = title
        self.pages = pages

    def __str__(self):  # 当你 print(book) 时,会调用这个
        return f"《{self.title}》- 共{self.pages}页"

    def __len__(self):  # 当你 len(book) 时,会调用这个
        return self.pages

    def __eq__(self, other):  # 比较两个 Book 对象
        if not isinstance(other, Book):
            return False
        return self.title == other.title and self.pages == other.pages

    def __add__(self, other):  # 实现 Book + Book
        if isinstance(other, Book):
            return Book(f"{self.title} + {other.title}", self.pages + other.pages)
        return NotImplemented

my_book = Book("Python 从入门到放弃", 999)
print(my_book)
print(f"这本书厚度:{len(my_book)}")
book2 = Book("Python 从入门到放弃", 999)
print(my_book == book2)  # True
combined = my_book + book2
print(combined)  # 《Python 从入门到放弃 + Python 从入门到放弃》- 共 1998 页

9. 综合实战:简易 RPG 战斗系统

咱们来个硬核的,用 OOP 写个简单的游戏角色对战逻辑。

import random

class Character:
    def __init__(self, name, hp, ad):
        self.name = name
        self.hp = hp
        self.ad = ad  # 攻击力

    def is_alive(self):
        return self.hp > 0

    def attack(self, target):
        damage = self.ad + random.randint(-5, 5)
        print(f"【{self.name}】发起攻击,对【{target.name}】造成了 {damage} 点伤害!")
        target.receive_damage(damage)

    def receive_damage(self, damage):
        self.hp -= damage
        if self.hp < 0:
            self.hp = 0
        print(f"【{self.name}】剩余血量:{self.hp}")

class Warrior(Character):
    def __init__(self, name):
        super().__init__(name, hp=150, ad=20)  # 战士血厚

    def receive_damage(self, damage):
        # 战士有 20% 几率格挡
        if random.random() < 0.2:
            print(f"【{self.name}】格挡了攻击!")
            damage = damage // 2
        super().receive_damage(damage)

class Mage(Character):
    def __init__(self, name):
        super().__init__(name, hp=80, ad=45)  # 法师攻高

    def attack(self, target):
        # 法师有 30% 几率暴击
        if random.random() < 0.3:
            damage = int(self.ad * 1.5) + random.randint(-5, 5)
            print(f"【{self.name}】发动暴击!")
        else:
            damage = self.ad + random.randint(-5, 5)
            print(f"【{self.name}】发起攻击,对【{target.name}】造成了 {damage} 点伤害!")
        target.receive_damage(damage)

# 开始战斗
p1 = Warrior("盖伦")
p2 = Mage("拉克丝")
print("--- 战斗开始 ---")
while p1.is_alive() and p2.is_alive():
    p1.attack(p2)
    if not p2.is_alive():
        print(f"\n恭喜【{p1.name}】获得胜利!")
        break
    p2.attack(p1)
    if not p1.is_alive():
        print(f"\n恭喜【{p2.name}】获得胜利!")
        break

10. 注意事项

  1. 别为了 OOP 而 OOP:如果一个功能写个函数就能搞定,千万别强行封装个类。过度设计是万恶之源。
  2. 优先使用组合而非继承:如果你发现继承树超过 3 层,说明你的架构出问题了。记住:"Has-a" 往往比 "Is-a" 更灵活。
  3. self 别漏写:在类的方法里访问属性,必须带 self.,这是新手最容易犯的低级错误。
  4. 理解 MRO 再使用多重继承:多重继承很强大,但如果不理解 MRO,很容易踩坑。
  5. 慎用元类:元类是强大的工具,但会增加代码复杂度,只在必要时使用。

11. 总结与实践

OOP 是设计出来的,不是写出来的。动动手,感受一下全局视角。

目录

  1. 1. 核心问题:为什么我们要搞 OOP?
  2. 2. 基础:类(Class)与对象(Object)
  3. 2.1 模具与成品
  4. 造两个机器人
  5. 2.2 self 的作用
  6. 2.3 类与对象的内存布局
  7. 内存布局分析
  8. 类属性是所有实例共享的
  9. 修改类属性会影响所有实例
  10. 但给实例赋值会创建实例属性,不影响其他实例
  11. 3. 三大特性:封装、继承、多态
  12. 3.1 封装 (Encapsulation):把脏活累活藏起来
  13. print(account.__balance) # 这行会报错!
  14. 3.2 继承 (Inheritance):少写代码的捷径
  15. 3.3 多态 (Polymorphism):鸭子类型
  16. 4. 深入:MRO(方法解析顺序)算法详解
  17. 4.1 什么是 MRO
  18. 查看 MRO
  19. 4.2 C3 线性化算法的核心规则
  20. 复杂继承示例
  21. MRO: Combined -> Mixin1 -> Mixin2 -> Base -> object
  22. 输出:
  23. Combined greet
  24. Mixin1 greet
  25. Mixin2 greet
  26. Base greet
  27. 4.3 使用 super() 的正确姿势
  28. 输出顺序:D -> B -> C -> A
  29. 注意:A 的 super() 调用 object.init(),什么都不做
  30. 5. 进阶:描述符(Descriptor)机制
  31. 5.1 什么是描述符
  32. p.age = 200 # ValueError: age must be between 0 and 150
  33. p.salary = "high" # TypeError: salary must be a number
  34. 5.2 描述符的类型
  35. 非数据描述符:实例属性优先
  36. 数据描述符:描述符优先
  37. 5.3 property 的本质
  38. property 的简化版实现
  39. 使用自定义 Property
  40. 6. 进阶:属性查找机制
  41. 6.1 属性查找顺序
  42. 如果描述符没有 set
  43. 6.2 自定义属性访问
  44. 7. 进阶:元类(Metaclass)入门
  45. 7.1 什么是元类
  46. MyClass 是 type 的实例
  47. 等价于 MyClass2 = type('MyClass2', (), {})
  48. 7.2 自定义元类
  49. 7.3 元类的 new 和 init
  50. 7.4 元类的实际应用
  51. 8. 魔法方法:让你的类更"Pythonic"
  52. 9. 综合实战:简易 RPG 战斗系统
  53. 开始战斗
  54. 10. 注意事项
  55. 11. 总结与实践

更多推荐文章

查看全部
  • AI 与 Apache ECharts 结合生成专业数据可视化图表
  • DeepSeek + 通义万相高效制作 AI 视频实战详解
  • Python 文件操作详解:读写模式、序列化与路径管理
  • Python Web 开发平台:FastAPI 与 Django 双后端架构方案
  • Z-Image-Turbo 与 Stable Diffusion XL 对比评测
  • Python 属性描述符:原理剖析与 ORM 实战
  • ComfyUI Mixlab 插件 Whisper.available False 报错修复方案
  • C++ RAII 与智能指针详解
  • Python 学习过程中的核心难点与分阶段突破指南
  • 基于 DeepSeek 的贪吃蛇游戏开发实战
  • Python 爬虫入门实战指南
  • 前端文件下载实战:从原理到最佳实践
  • Stable Diffusion:AI 图像生成技术原理与应用
  • 【实践】操作系统智能助手OS Copilot新功能测评
  • IDEA 迁移至 Trae AI IDE:SpringBoot+Maven+Vue 项目配置
  • Windows Python 3.13 安装与配置详细教程
  • Python 连接和操作 Elasticsearch 指南
  • 检索增强生成 (RAG) 原理与 LangChain LlamaIndex 实践
  • EasyOCR 入门指南:Python 开源 OCR 工具使用与实战
  • 前端 WebSocket 实时通信:替代轮询的最佳实践

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online