← 返回题目列表

Python 元类是什么?type 和 metaclass 有什么关系?

高频 困难 第 15 / 21 题 更新于 2026/07/27
元类typemetaclass类创建

简化版

元类是创建类的类。普通对象由类创建,而类对象通常由 type 创建;通过自定义 metaclass,可以干预类创建过程,例如注册子类、校验类定义、给类自动添加属性或方法。

详细版

在 Python 中:

class User:
    pass

u = User()

uUser 的实例,而 User 本身也是对象:

print(type(u))     # <class '__main__.User'>
print(type(User))  # <class 'type'>

这说明普通类对象通常是由 type 创建的。

自定义元类:

class ModelMeta(type):
    def __new__(mcls, name, bases, namespace):
        namespace["table_name"] = name.lower()
        return super().__new__(mcls, name, bases, namespace)

class User(metaclass=ModelMeta):
    pass

print(User.table_name)  # user

元类适合框架底层,不适合普通业务里随意使用。很多需求可以用类装饰器、__init_subclass__ 或普通继承解决。

完整版教学

一、先理解类也是对象

Python 里几乎一切都是对象,类也不例外。

class User:
    pass

执行类定义时,Python 会创建一个类对象 User

可以把类赋值给变量:

Alias = User
u = Alias()

也可以查看类的类型:

print(type(User))  # type

既然类是对象,就会有“谁创建了类对象”这个问题。答案通常是 type

二、type 的两个角色

type 常见用法是查看对象类型:

type(123)
type("abc")

type 也可以动态创建类:

User = type("User", (), {"role": "guest"})

这大致等价于:

class User:
    role = "guest"

三个参数含义:

  • 类名;
  • 父类元组;
  • 类命名空间字典。

这说明 class 语句背后也会经历类对象创建过程。

三、元类如何介入类创建

自定义元类通常继承 type

class Meta(type):
    def __new__(mcls, name, bases, namespace):
        print("creating", name)
        return super().__new__(mcls, name, bases, namespace)

使用:

class User(metaclass=Meta):
    pass

当 Python 创建 User 类对象时,会调用 Meta.__new__

你可以在这里检查类定义、修改命名空间、注册类信息。

四、元类适合做什么

常见用途:

  • ORM 收集字段定义;
  • 插件系统自动注册子类;
  • 校验类必须定义某些属性;
  • 自动生成类方法;
  • 框架级声明式 API。

例如简单注册:

registry = {}

class RegistryMeta(type):
    def __new__(mcls, name, bases, namespace):
        cls = super().__new__(mcls, name, bases, namespace)
        if name != "Base":
            registry[name] = cls
        return cls

class Base(metaclass=RegistryMeta):
    pass

class User(Base):
    pass

创建 User 类时,它会自动进入 registry

五、元类为什么要谨慎

元类强大,但也增加理解成本。

问题包括:

  • 类创建逻辑隐藏,不容易追踪;
  • 多个元类难以组合;
  • 调试门槛高;
  • 普通业务开发者不一定熟悉;
  • 很多场景有更简单替代方案。

如果只是想在子类创建时做一点注册,可以考虑 __init_subclass__

class Base:
    registry = []

    def __init_subclass__(cls):
        super().__init_subclass__()
        Base.registry.append(cls)

如果只是想改一个类,可以用类装饰器。

六、元类和类装饰器的区别

类装饰器在类创建完成后处理类:

def add_table_name(cls):
    cls.table_name = cls.__name__.lower()
    return cls

@add_table_name
class User:
    pass

元类是在类创建过程中介入。它能控制得更早、更底层。

选择建议:

  • 简单修改类:类装饰器;
  • 子类创建钩子:__init_subclass__
  • 框架级类创建控制:元类。

七、常见误区与追问

记忆钩子:实例由类创建,类由元类创建。普通业务代码大多改“实例行为”,框架底层才常改“类是怎么被创建出来的”。

  • 误区:元类就是父类。 父类参与实例方法和属性继承,元类负责创建类对象;class User(Base) 里的 Base 是父类,metaclass=Meta 才是指定元类。
  • 追问:type 为什么既能看类型又能造类? type(obj) 返回对象的类型;type(name, bases, namespace) 可以动态创建类,这体现了类本身也是对象。
  • 误区:元类适合解决普通业务复用。 元类会影响类创建流程,理解成本高;普通复用优先考虑函数、组合、类装饰器或 __init_subclass__
  • 追问:ORM 为什么常用元类? ORM 需要在类定义阶段收集字段、生成映射、注册模型元信息;这些都发生在类对象创建时,元类可以统一拦截。
  • 误区:类装饰器和元类完全等价。 类装饰器处理已经创建好的类,元类能参与类创建前后的更底层流程,例如准备命名空间、校验基类、控制类对象生成。
  • 追问:元类冲突是什么? 多个父类如果来自不兼容的元类,Python 无法确定子类该由哪个元类创建,就可能出现 metaclass conflict,需要设计共同元类或调整继承结构。

八、加强记忆

对象由类创建,类通常由 type 创建,元类就是创建类的类。metaclass 能干预类定义变成类对象的过程,很适合框架底层,但业务代码里要谨慎。能用类装饰器或 __init_subclass__ 解决,就别急着上元类。