欢迎光临
我们一直在努力

【Python全栈开发】第4讲 | 面向对象进阶:从“封装继承”到“设计模式”的蜕变

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 # 实例属性(存在实例对象中)
self.age = age

def greet(self): # 实例方法
return f"Hello, I'm {self.name}"

# 内存布局分析
p1 = Person("Alice", 25)
p2 = Person("Bob", 30)

# 类属性是所有实例共享的
print(Person.species) # Homo sapiens
print(p1.species) # Homo sapiens(通过实例访问类属性)
print(p2.species) # Homo sapiens

# 修改类属性会影响所有实例
Person.species = "Human"
print(p1.species) # Human

# 但给实例赋值会创建实例属性,不影响其他实例
p1.species = "Modified"
print(p1.species) # Modified(实例属性遮蔽类属性)
print(p2.species) # Human(仍然访问类属性)
print(Person.species) # 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 线性化算法的核心规则

  • 子类优先于父类:先查找子类,再查找父类
  • 单调性:如果 C 是 D 的父类,那么在 C 的 MRO 中出现的类,在 D 的 MRO 中也以相同顺序出现
  • 从左到右:按照继承列表中的顺序查找
  • # 复杂继承示例
    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 按以下顺序查找:

  • 数据描述符:在 obj.__class__ 中查找 attr,如果是数据描述符,返回描述符的值
  • 实例字典:在 obj.__dict__ 中查找 attr
  • 非数据描述符:在 obj.__class__ 及其父类的 __dict__ 中查找 attr
  • 类属性:在 obj.__class__ 及其父类的 __dict__ 中查找 attr
  • __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', (), {})
    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="alice@example.com")
    user.save()
    # Saving User: {'id': 1, 'name': 'Alice', 'email': 'alice@example.com'}


    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. 避坑小贴士(老司机的叮嘱)

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

  • 11. 实战演练:巩固你的内功

    OOP 是设计出来的,不是写出来的。动动手,感受一下"上帝视角"。

    赞(0)
    未经允许不得转载:171主机测评 » 【Python全栈开发】第4讲 | 面向对象进阶:从“封装继承”到“设计模式”的蜕变
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址