本文为《Python全栈修炼之路》第20篇《元类与Python对象模型深度解析》的配套练习。 六道题目由易到难,覆盖 type 双重身份、类创建生命周期、C3 线性化、元类三剑客、__prepare__ 命名空间定制、__set_name__ 协议等核心知识点。每道题均包含完整题目描述、详细解题思路、关联知识点和可直接运行的参考代码。
题目一:用 type() 动态创建带继承的类
题目描述
不使用 class 关键字,仅通过 type() 函数动态创建一个名为 Student 的类,要求:
解题思路
本题考察 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 的元类,使得所有使用该元类的类在被定义时,自动获得以下两个类属性:
要求:
- 通过元类的 __new__ 方法实现
- 不修改类的原有定义(对使用者透明)
解题思路
本题考察元类 __new__ 方法的基本使用。核心思路:
注意:也可以在调用 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}\”)
题目三:实现插件自动注册元类
题目描述
设计一个插件系统,要求:
解题思路
本题考察元类在类创建阶段的拦截能力,是 Django ORM 和 SQLAlchemy 中字段收集模式的简化版。
核心设计:
关联知识点
| 元类 __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 =</




