当前位置: 首页 > news >正文

Python - 元类

元类(metaclass)是 “创建类的类”。
在 Python 中,一切皆对象,类本身也是对象;既然类是对象,它必然由某个“东西”创建——这个“东西”就是元类。默认情况下,这个“东西”是内置的 type

元类让你“在类诞生之前”就能插手它的创建过程

⭐面试
type作用:
一、获取对象的类型
二、创建一个新类(用于创建类的元类)

# type第一个参数是类名
# type第二个参数是父类列表
# type第三个参数是类拥有的内容MyClass = type("Person",(list,object),{"a":lambda self:print("a桀"),"__doc__":"这是一个注释!!!"})print(MyClass.__name__,callable(MyClass))
print(MyClass.__doc__)
print(dir(MyClass))
mc = MyClass()
mc.a()

 实现单一实例:

class MyType(type):instance1 = {}def __new__(cls, name,bases,dit ):name = "gg"+nameinstance2 = super().__new__(cls , name,bases,dit)return instance2def __call__(cls, *args, **kwargs):if cls not in cls.instance1:cls.instance1[cls] = super().__call__( *args, **kwargs)return cls.instance1class Penson(metaclass=MyType):passp1 =Penson()
p2 =Penson()
print(p1 is p2)
print(Penson.__name__)

 

进阶

当你需要对所有类或某类家族做统一、不可绕过的改造时,元类是最干净、最强制、最 DRY 的办法。

1. 强制编码规范(公司级代码审计)

需求:所有业务 Model 必须带 created_at / updated_at,且字段名不能错。

class AuditMeta(type):def __new__(mcs, name, bases, ns):if bases:                       # 跳过基类自身for f in ('created_at', 'updated_at'):if f not in ns.get('__annotations__', {}):raise TypeError(f'{name} 缺少字段 {f}')return super().__new__(mcs, name, bases, ns)class BaseModel(metaclass=AuditMeta):passclass User(BaseModel):created_at: datetimeupdated_at: datetime
# 少写任何一个字段直接抛 TypeError,CI 阶段就挂掉。

 2. 自动生成重复代码(DRY)

需求:每个 ORM 实体类都要实现 to_dict()

class DictableMeta(type):def __new__(mcs, name, bases, ns):def to_dict(self):return {k: getattr(self, k) for k in self.__annotations__}ns['to_dict'] = to_dictreturn super().__new__(mcs, name, bases, ns)class User(metaclass=DictableMeta):id: intname: stru = User(); u.id=1; u.name='tim'
print(u.to_dict())      # {'id': 1, 'name': 'tim'}

 3. 单例模式(最保险的实现)

class SingletonMeta(type):_inst = {}def __call__(cls, *a, **kw):if cls not in cls._inst:cls._inst[cls] = super().__call__(*a, **kw)return cls._inst[cls]class Config(metaclass=SingletonMeta):pass

4. 自动注册插件 / 命令 / 路由

 需求:把散落在各文件里的 Command 子类自动塞进全局字典,免去手动 import。

registry = {}class CommandMeta(type):def __init__(cls, name, bases, ns):super().__init__(name, bases, ns)if bases:                 # 过滤基类registry[cls.__name__] = clsclass Command(metaclass=CommandMeta):passclass GitPull(Command): ...
class DockerBuild(Command): ...print(registry)   # {'GitPull': <class 'GitPull'>, 'DockerBuild': <class 'DockerBuild'>}

 5. 不可变类(冻结属性)

class FrozenMeta(type):def __new__(mcs, name, bases, ns):ns['__slots__'] = tuple(ns.get('__annotations__', {}))ns['__setattr__'] = lambda self, k, v: (_ for _ in ()).throw(AttributeError('frozen'))return super().__new__(mcs, name, bases, ns)class Point(metaclass=FrozenMeta):x: inty: int

 6. 枚举值自动校验

class EnumMeta(type):def __new__(mcs, name, bases, ns):choices = {v for k, v in ns.items() if not k.startswith('_')}ns['_choices'] = choicesreturn super().__new__(mcs, name, bases, ns)class Color(metaclass=EnumMeta):RED = 1GREEN = 2BLUE = 3print(Color._choices)   # {1, 2, 3}

 7. 自动加日志 / 权限检查

def logged(fn):def wrapper(*a, **kw):print(f'[LOG] {fn.__name__}')return fn(*a, **kw)return wrapperclass APIMeta(type):def __new__(mcs, name, bases, ns):for k, v in list(ns.items()):if callable(v) and not k.startswith('_'):ns[k] = logged(v)return super().__new__(mcs, name, bases, ns)class UserAPI(metaclass=APIMeta):def get(self, uid): ...def post(self, data): ...

 8. 字段别名 / 数据库映射

class MapperMeta(type):def __new__(mcs, name, bases, ns):ns['_db_map'] = {k: k.replace('_', '').lower() for k in ns.get('__annotations__', {})}return super().__new__(mcs, name, bases, ns)

9. 防止子类忘记调用 super() 

class EnsureSuperMeta(type):def __init__(cls, name, bases, ns):if bases and '__init__' in ns:src = ns['__init__'].__code__.co_namesif 'super' not in src:raise TypeError(f'{name}.__init__ 必须调用 super()')super().__init__(name, bases, ns)

 

10. 框架级 API 设计(Django ORM、SQLAlchemy、Pydantic)

这些库的核心就是元类:

  • Django Model:把类属性翻译成数据库列。

  • Pydantic:根据类型注解生成校验器。

  • Marshmallow:自动生成序列化/反序列化逻辑。

示例(极简 ORM):

 

class Field:def __init__(self, typ): self.typ = typclass ORMMeta(type):def __new__(mcs, name, bases, ns):ns['_fields'] = {k: v.typ for k, v in ns.items() if isinstance(v, Field)}return super().__new__(mcs, name, bases, ns)class Table(metaclass=ORMMeta):id = Field(int)name = Field(str)print(Table._fields)   # {'id': <class 'int'>, 'name': <class 'str'>}

自定义元类

如果你想在 “类创建时” 插手逻辑(比如自动加前缀、注册子类等),可以继承 type 并重写 __new____init__

class Meta(type):def __new__(mcs, name, bases, namespace, **kw):print(f'正在创建类 {name}')# 可以在 namespace 里改、删、增属性namespace['__slots__'] = ('x', 'y')  # 强制所有子类带 __slots__return super().__new__(mcs, name, bases, namespace)class Point(metaclass=Meta):pass

 什么时候该用元类?

场景解决方案优先级
只对单个类做装饰类装饰器
需要 所有子类 都遵守同一规则元类
运行期动态拼装类type() 直接构造
框架级统一行为(ORM、API、校验)元类

元类 vs 类装饰器

目的元类类装饰器
拦截并修改类创建过程✅ 在类对象诞生前插手❌ 类对象诞生后包裹
对继承链生效(子类也走逻辑)✅ 自动❌ 需要手动再装饰子类
实现复杂度较高较低
http://www.lryc.cn/news/605117.html

相关文章:

  • 离散扩散模型在数独问题上的复现与应用
  • RAG工作流程总览
  • 解析非法获取计算机信息系统数据罪中的其他技术手段
  • 《超级秘密文件夹》密码遗忘?试用版/正式版找回教程(附界面操作步骤)
  • IATF 16949详解(腾讯混元)
  • Oracle11g数据库迁移达梦8数据库方案
  • 论文阅读|CVPR 2025|Mamba进一步研究|GroupMamba
  • 领域驱动设计(DDD)在分布式系统中的架构实践
  • cpp实现音频重采样8k->16k及16k->8k
  • 不同环境安装配置redis
  • 网络端口号全景解析:从基础服务到特殊应用的完整指南
  • 代码随想录算法训练营第三十六天
  • 【git】GitHub 的专用代理地址
  • day21-Excel文件解析
  • uvm-tlm-port-export-imp
  • 在VS2022中调试ASP.NET项目时修改DLL或ASPX动态页面的原理及实现方法
  • STM32CubeIDE新建项目过程记录备忘(二) GPIO输出demo:LED闪烁
  • 2025 IT专业人才培养趋势与职业发展指南:技术+数据复合型能力的构建路径
  • 【Kubernetes 指南】基础入门——Kubernetes 201(一)
  • OpenEuler 安装 apache + php8 不解析php文件的处理
  • 微信小程序中实现页面跳转的方法
  • Python奇幻之旅:从零开始的编程冒险
  • cpp-httplib 线程安全
  • mybatis中的极易出现错误用法
  • Chroma安装教程
  • uni-app webview的message监听不生效(uni.postmessage is not a function)
  • 明智运用C++异常规范(Exception Specifications)
  • 监测预警系统:让园区更高效、更安全、更智能
  • [Python] -进阶理解10- 用 Python 实现简易爬虫框架
  • Android Animation Transitions:打造流畅的用户体验