欢迎光临
我们一直在努力

Python 元类与Python对象模型:6 道编程题从入门到精通

本文为《Python全栈修炼之路》第20篇《元类与Python对象模型深度解析》的配套练习。 六道题目由易到难,覆盖 type 双重身份、类创建生命周期、C3 线性化、元类三剑客、__prepare__ 命名空间定制、__set_name__ 协议等核心知识点。每道题均包含完整题目描述、详细解题思路、关联知识点和可直接运行的参考代码。


题目一:用 type() 动态创建带继承的类

题目描述

不使用 class 关键字,仅通过 type() 函数动态创建一个名为 Student 的类,要求:

  • 继承自一个预先定义的 Person 类(Person 有 __init__(self, name, age) 和 introduce(self) 方法)
  • Student 类新增一个类属性 school = \”Python Academy\”
  • Student 类新增一个实例方法 study(self, subject),返回 f\”{self.name} 正在学习 {subject}\”
  • 创建 Student 实例后,能正常调用继承的 introduce 方法和自己的 study 方法
  • 解题思路

    本题考察 type 的三参数形式 type(name, bases, namespace):

    • 第一个参数 name:类名字符串 \”Student\”
    • 第二个参数 bases:基类元组 (Person,),实现继承
    • 第三个参数 namespace:类属性字典,包含类属性和方法

    关键点在于方法的定义。namespace 中的函数会成为类的方法,第一个参数必须是 self。我们需要先定义好函数,再将其放入字典中传给 type()。

    关联知识点

    知识点
    说明
    type(name, bases, namespace) type 的三参数形式,动态创建类
    类属性字典 namespace 中直接放值即为类属性
    方法绑定 namespace 中放函数即为实例方法
    继承机制 bases 元组指定父类,自动继承父类方法

    参考代码

    # ========== 第一步:定义父类 Person ==========
    class Person:
    def __init__(self, name, age):
    self.name = name
    self.age = age

    def introduce(self):
    return f\”我是 {

    self.name},今年 {

    self.age} 岁\”

    # ========== 第二步:定义 Student 需要的方法 ==========
    def student_init(self, name, age, grade):
    \”\”\”Student 的初始化方法,调用父类 __init__ 并新增 grade 属性\”\”\”
    Person.__init__(self, name, age)
    self.grade = grade

    def study(self, subject):
    \”\”\”学习方法,返回学习信息字符串\”\”\”
    return f\”{

    self.name} 正在学习 {

    subject}\”

    # ========== 第三步:用 type() 动态创建 Student 类 ==========
    Student = type(
    \”Student\”, # 类名
    (Person,), # 基类元组,继承 Person
    {

    # 类属性字典(命名空间)
    \”school\”: \”Python Academy\”, # 类属性
    \”__init__\”: student_init, # 实例方法(覆盖父类)
    \”study\”: study, # 新增实例方法
    }
    )

    # ========== 第四步:验证 ==========
    s = Student(\”Alice\”, 20, \”大三\”)

    # 验证类属性
    print(f\”学校: {

    Student.school}\”) # 学校: Python Academy

    # 验证继承的方法
    print(s.introduce()) # 我是 Alice,今年 20 岁

    # 验证自己的方法
    print(s.study(\”Python元类\”)) # Alice 正在学习 Python元类

    # 验证实例属性
    print(f\”年级: {

    s.grade}\”) # 年级: 大三

    # 验证类型关系
    print(f\”Student 的类型: {

    type(Student)}\”) # <class \’type\’>
    print(f\”s 的类型: {

    type(s)}\”) # <class \’__main__.Student\’>
    print(f\”是否是 Person 的实例: {

    isinstance(s, Person)}\”) # True


    题目二:实现自动添加创建时间戳的元类

    题目描述

    编写一个名为 TimestampMeta 的元类,使得所有使用该元类的类在被定义时,自动获得以下两个类属性:

  • _created_at:类创建时的 Unix 时间戳(精确到秒,浮点数)
  • _created_iso:类创建时的 ISO 格式时间字符串(如 \”2024-01-15T10:30:00\”)
  • 要求:

    • 通过元类的 __new__ 方法实现
    • 不修改类的原有定义(对使用者透明)

    解题思路

    本题考察元类 __new__ 方法的基本使用。核心思路:

  • 定义 TimestampMeta 继承自 type
  • 在 __new__ 中,先调用 super().__new__() 创建类对象
  • 类对象创建后,直接在类对象上设置 _created_at 和 _created_iso 属性
  • 返回修改后的类对象
  • 注意:也可以在调用 super().__new__() 之前修改 namespace 字典来添加属性,但直接在创建后的类对象上赋值更直观。

    关联知识点

    知识点
    说明
    元类 __new__ 控制类对象的创建过程
    super().__new__() 必须调用以完成实际的类创建
    类属性注入 在 __new__ 中直接给 cls 赋值
    time.time() 获取 Unix 时间戳
    datetime.fromtimestamp() 将时间戳转为可读格式

    参考代码

    import time
    from datetime import datetime

    # ========== 定义元类 ==========
    class TimestampMeta(type):
    \”\”\”自动为类添加创建时间戳的元类\”\”\”

    def __new__(mcs, name, bases, namespace, **kwargs):
    # 1. 调用 type.__new__ 完成类对象的创建
    cls = super().__new__(mcs, name, bases, namespace, **kwargs)

    # 2. 在已创建的类对象上注入时间戳属性
    now = time.time()
    cls._created_at = now
    cls._created_iso = datetime.fromtimestamp(now).isoformat()

    # 3. 返回类对象
    return cls

    # ========== 使用元类定义类 ==========
    class User(metaclass=TimestampMeta):
    \”\”\”用户类\”\”\”
    def __init__(self, username):
    self.username = username

    class Order(metaclass=TimestampMeta):
    \”\”\”订单类\”\”\”
    def __init__(self, order_id):
    self.order_id = order_id

    # ========== 验证 ==========
    print(f\”User 创建时间戳: {

    User._created_at}\”)
    print(f\”User ISO格式: {

    User._created_iso}\”)
    print()
    print(f\”Order 创建时间戳: {

    Order._created_at}\”)
    print(f\”Order ISO格式: {

    Order._created_iso}\”)
    print()

    # 验证实例化不受影响
    u = User(\”alice\”)
    print(f\”用户名: {

    u.username}\”) # alice
    print(f\”User 有 _created_at: {

    hasattr(User, \’_created_at\’)}\”) # True

    # 验证子类自动继承元类
    class VipUser(User):
    \”\”\”VIP用户,自动继承 TimestampMeta\”\”\”
    level = \”VIP\”

    print(f\”VipUser 有 _created_at: {

    hasattr(VipUser, \’_created_at\’)}\”) # True
    print(f\”VipUser ISO格式: {

    VipUser._created_iso}\”)


    题目三:实现插件自动注册元类

    题目描述

    设计一个插件系统,要求:

  • 定义一个 PluginMeta 元类,当子类被定义时,自动将其注册到一个全局注册表中
  • 注册表的键为插件的 plugin_name(类属性),值为类本身
  • 如果子类没有定义 plugin_name,则使用类名的小写形式作为默认值
  • 基类 BasePlugin 本身不应被注册
  • 提供一个 get_plugin(name) 函数,根据名称从注册表中获取插件类
  • 提供一个 list_plugins() 函数,列出所有已注册的插件
  • 解题思路

    本题考察元类在类创建阶段的拦截能力,是 Django ORM 和 SQLAlchemy 中字段收集模式的简化版。

    核心设计:

  • 注册表存储位置:将注册表作为元类的类属性(PluginMeta.registry),而非实例属性。这样所有使用该元类的类共享同一个注册表。
  • 跳过基类:在 __new__ 中通过判断 name 是否为基类名来跳过注册。更健壮的做法是检查当前类是否就是 BasePlugin 本身。
  • 默认 plugin_name:使用 namespace.get(\’plugin_name\’, name.lower()) 获取,如果未定义则用类名小写。
  • 工厂函数:get_plugin 从元类的注册表中查找并返回类,调用者可以进一步实例化。
  • 关联知识点

    知识点
    说明
    元类 __new__ 拦截 在类创建时收集信息
    元类类属性作为注册表 注册表挂在元类上,所有子类共享
    namespace.get() 从命名空间中安全获取属性
    工厂模式 通过注册表 + 工厂函数实现运行时动态创建

    参考代码

    # ========== 定义元类 ==========
    class PluginMeta(type):
    \”\”\”插件自动注册元类\”\”\”

    # 注册表:挂在元类上,所有使用该元类的类共享
    registry = {

    }

    def __new__(mcs, name, bases, namespace, **kwargs):
    # 1. 先创建类对象
    cls = super().__new__(mcs, name, bases, namespace, **kwargs)

    # 2. 跳过基类 BasePlugin 本身
    # 判断依据:bases 为空(没有父类)或 name 为特定基类名
    is_base = not bases or name == \”BasePlugin\”
    if is_base:
    return cls

    # 3. 获取 plugin_name,未定义则使用类名小写
    plugin_name = namespace.get(\”plugin_name\”, name.lower())

    # 4. 注册到注册表
    mcs.registry[plugin_name] = cls
    print(f\”[注册] 插件 \’{

    plugin_name}\’ -> {

    name}\”)

    return cls

    # ========== 定义插件基类 ==========
    class BasePlugin(metaclass=PluginMeta):
    \”\”\”插件基类,不会被注册\”\”\”
    plugin_name =</

    赞(0)
    未经允许不得转载:171主机测评 » Python 元类与Python对象模型:6 道编程题从入门到精通
    分享到: 更多 (0)

    评论 抢沙发

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