Python3 基础教程

Python3 元编程(Metaprogramming)

🎉摘要:本文深入剖析Python元编程核心机制,涵盖type()动态创建类、元类(metaclass)控制类创建过程、描述符协议拦截属性访问,并结合单例、自动注册、命名规范、微型ORM等实战案例,展示元编程在框架开发中的强大应用。

元编程,简单来说就是用代码操控代码。Python 拥有丰富的元编程工具:type() 可以动态生成类;元类(metaclass)能够干预类的创建全过程;描述符协议则实现对属性访问行为的拦截。

我们日常使用的 @property、各类 ORM 框架、类装饰器等高级特性,底层实现都依赖这套元编程机制。

类也是对象:理解 type 的双重身份

在 Python 中一切皆对象,类本身同样是对象。

既然类是对象,那它必然由某个类实例化而来,这个 “制造类的类” 就是 type。

type 拥有双重身份:

(1)普通内置函数:type(obj),传入一个对象,返回该对象的类型。

print(type(123))   # int
print(type("hi"))  # str

(2)元类(metaclass):type 是所有普通类默认的元类,负责创建类。语法:

type(类名, 父类元组, 命名空间字典)

使用 type() 可以动态构建类。

# 和 class User: 定义效果等价
User = type("User", (), {"name": "test"})

底层逻辑:

class User:
    pass

# User 这个类对象,是 type 的实例
print(type(User))   # type
# int、str 等内置类型,同样是 type 的实例
print(type(int))    # type

其中,对象由类创建,类由元类 type 创建。而 type 自身是自己的实例,即 type(type) 等于 type(元编程闭环)。

日常的 class 语法只是语法糖,Python 执行 class 语句时,底层调用元类 type 来实例化生成类对象。

而自定义元类,本质就是继承 type,重写类创建逻辑,以此拦截、修改类的构建过程。

type() 动态创建类

type() 语法如下:

type(name, bases, namespace)

等价于 class 关键字定义类,class 只是语法糖,底层最终调用该形式的 type。

参数详解:

  • name: str  类的名称,对应类的 __name__ 属性。例如:

Cls = type("Demo", (), {})
print(Cls.__name__)  # Demo
  • bases: tuple [类,...]  父类元组,对应类的继承列表 __bases__,空元组代表不继承任何类(默认继承 object)。例如:

class Base:
    pass

# 继承 Base
Sub = type("Sub", (Base,), {})
print(Sub.__bases__)  # (Base,)
  • namespace: dict   类命名空间字典,存放类属性、方法、特殊魔法属性,最终成为类的 __dict__。

示例:使用 type() 创建一个类

# 传统写法
class Dog:
    species = "Canis"
    def __init__(self, name):
        self.name = name
        
    def bark(self):
        return f"{self.name} says woof!"

# 等价于 type() 动态创建
Dog2 = type(
    "Dog",                              # 类名
    (object,),                          # 父类元组
    {
        "species": "Canis",
        "__init__": lambda self, name: setattr(self, "name", name),
        "bark": lambda self: f"{self.name} says woof!",
    },
)

d1 = Dog("Buddy")
print(d1.bark())           # Buddy says woof!
print(d1.species)          # Canis
print(type(d1))            # <class '__main__.Dog'>

d2 = Dog2("Buddy")
print(d2.bark())           # Buddy says woof!
print(d2.species)          # Canis
print(type(d2))            # <class '__main__.Dog'>

运行示例,输出:

Buddy says woof!
Canis
<class '__main__.Dog'>
Buddy says woof!
Canis
<class '__main__.Dog'>

动态创建带继承的类

下面示例利用内置 type() 动态创建类,新类自动继承 Animal 类,工厂函数 make_species_class 批量生成不同动物物种类。代码如下:

# 父类:动物基类
class Animal:
    # 类属性:动物界
    kingdom = "Animalia"

    def __init__(self, name):
        """构造方法,初始化动物名称"""
        self.name = name


# 工厂函数:动态创建继承 Animal 的子类
def make_species_class(species_name, sound):
    """
    工厂函数:根据物种名动态创建继承Animal的类
    :param species_name: 物种类名
    :param sound: 该物种的叫声
    :return: 动态生成的物种类
    """
    def __init__(self, name):
        # 调用父类构造方法
        Animal.__init__(self, name)
        # 保存当前物种对应的叫声
        self.sound = sound

    def speak(self):
        """实例方法:发出叫声"""
        return f"{self.name} says {self.sound}"

    # type(类名, 父类元组, 命名空间字典) 动态构建类
    return type(
        species_name,       # 生成的类名称
        (Animal,),          # 继承的父类元组,实现继承
        {
            "__init__": __init__,    # 绑定构造函数
            "speak": speak,          # 绑定实例方法
            "_species": species_name # 自定义类属性,标记物种名称
        },
    )


# 动态生成 Cat 类、Dog 类
Cat = make_species_class("Cat", "meow")
Dog = make_species_class("Dog", "woof")

# 实例化对象
c = Cat("Kitty")
d = Dog("Buddy")

print(c.speak())              # Kitty says meow
print(d.speak())              # Buddy says woof
print(c.kingdom)              # Animalia(继承自 Animal 的类属性)
print(isinstance(c, Animal))  # True,Cat实例属于Animal类型
print(issubclass(Cat, Dog))   # False,Cat与Dog是同级兄弟类,不存在继承关系

注意,Cat、Dog 是两个独立生成的子类,共同父类是 Animal,互为兄弟类,互相不存在继承关系。每个类自带独立的 speak 行为与 _species 标识。

运行时动态修改类

Python 的类、对象都是可变对象,程序运行过程中,可以随时增删、修改类属性、实例方法、静态方法、类方法,不需要重新定义 class,也不需要 type () 新建类。

示例:

class Calculator:
    def add(self, a, b):
        return a + b

c = Calculator()
print(c.add(1, 2))       # 3

# 运行时给类添加新方法
Calculator.sub = lambda self, a, b: a - b
print(c.sub(5, 3))       # 2(已有实例自动获得新方法)

# 运行时替换方法
Calculator.add = lambda self, a, b: a * b
print(c.add(1, 2))       # 2(方法已被替换)

# 动态添加属性到单个实例(不影响类)
c.factor = 10
print(c.factor)           # 10
# print(Calculator.factor)  # AttributeError

注意:

  • 动态修改类(Monkey Patch)只在测试打桩、热修复等场景,需要谨慎使用。

  • 生产代码中过度使用动态修改会降低可维护性。

描述符协议(Descriptor Protocol)

描述符 = 拦截器

描述符是实现了 __get__、__set__ 或 __delete__ 方法的类的实例。

当访问宿主实例属性时:

  • 读取属性前触发 __get__

  • 赋值属性前触发 __set__

  • del 删除属性前触发 __delete__

注意,不是属性赋值之后,是赋值操作发生时优先拦截,你可以在校验不通过时直接拒绝赋值。只有描述符挂载为宿主类属性,拦截逻辑才生效。如果放在 __init__ 当成实例属性,协议不会触发。

Python 的 @property、@staticmethod、@classmethod 底层都是描述符。

示例,演示描述符三协议方法的用法。

class Descriptor:
    """最简描述符示例"""
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        """
        instance: 被访问的实例(通过类访问时为 None)
        owner: 拥有该描述符的类
        """
        if instance is None:
            return f"<Descriptor {self.name} for {owner.__name__}>"
        return instance.__dict__.get(self.name, f"[default for {self.name}]")

    def __set__(self, instance, value):
        """拦截属性赋值"""
        print(f"[SET] {instance.__class__.__name__}.{self.name} = {value!r}")
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        """拦截属性删除"""
        print(f"[DELETE] {instance.__class__.__name__}.{self.name}")
        if self.name in instance.__dict__:
            del instance.__dict__[self.name]

class Person:
    # 类属性是描述符实例,所有 Person 实例共享同一个描述符对象
    name = Descriptor("name")
    age = Descriptor("age")


if __name__ == "__main__":
    p = Person()
    # 赋值触发 __set__
    p.name = "Alice"          # [SET] Person.name = 'Alice'
    p.age = 30                # [SET] Person.age = 30

    # 读取触发 __get__
    print(p.name)             # Alice
    print(p.age)              # 30

    # 删除触发 __delete__
    del p.name                # [DELETE] Person.name

    # 通过类访问
    print(Person.name)        # <Descriptor name for Person>

数据描述符 vs 非数据描述符

数据描述符(get + set)可读、可写双向拦截。赋值、取值都会经过描述符,优先级高于实例自身 __dict__,典型代表 @property。

非数据描述符(仅 get,没有 set)只拦截读取,不拦截赋值。一旦你手动给实例绑定同名属性,实例字典的值会覆盖描述符。典型代表 @staticmethod、@classmethod。

两者区别如下表:

类型条件优先级
数据描述符定义了 __set__ 或 __delete__高(实例字典也覆盖不了)
非数据描述符只定义了 __get__低(实例字典中的同名属性会覆盖它)

示例:

# 数据描述符(有 __set__)
class DataDescriptor:
    def __init__(self, name):
        self.name = name
    def __get__(self, instance, owner):
        return f"[DataDesc] {self.name}"
    def __set__(self, instance, value):
        print(f"[DataDesc] set {self.name} = {value}")

# 非数据描述符(只有 __get__)
class NonDataDescriptor:
    def __init__(self, name):
        self.name = name
    def __get__(self, instance, owner):
        return f"[NonDataDesc] {self.name}"

class Demo:
    data = DataDescriptor("data")
    nondata = NonDataDescriptor("nondata")

if __name__ == "__main__":
    d = Demo()
    # 数据描述符:实例字典无法覆盖
    print(d.data)             # [DataDesc] data
    d.__dict__["data"] = "instance value"
    print(d.data)             # 仍然是 [DataDesc] data(数据描述符优先级更高)

    # 非数据描述符:实例字典可以覆盖
    print(d.nondata)          # [NonDataDesc] nondata
    d.__dict__["nondata"] = "instance value"
    print(d.nondata)          # instance value(实例字典优先)

    # 删除实例字典后,恢复描述符行为
    del d.__dict__["nondata"]
    print(d.nondata)          # [NonDataDesc] nondata

Python 属性查找优先级(MRO):

  1. 数据描述符(定义了 __set__/__delete__)

  2. 实例字典 obj.__dict__

  3. 非数据描述符(只定义 __get__)

  4. 类字典 cls.__dict__ 及 MRO

示例 2:用描述符实现类型检查属性

from typing import Any

class Typed:
    """强制类型检查的描述符"""
    def __init__(self, name: str, expected_type: type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance: Any, owner: type) -> Any:
        if instance is None:
            return self
        return instance.__dict__.get(self.name)

    def __set__(self, instance: Any, value: Any) -> None:
        if not isinstance(value, self.expected_type):
            raise TypeError(
                f"属性 {self.name} 期望类型 {self.expected_type.__name__}, "
                f"实际得到 {type(value).__name__}"
            )
        instance.__dict__[self.name] = value

    def __delete__(self, instance: Any) -> None:
        del instance.__dict__[self.name]


class TypedMeta(type):
    """元类:自动为类属性添加 Typed 描述符"""
    # __new__ 负责开辟内存、产出全新实例;__init__ 只负责填充实例属性
    # 属性说明:
    # mcs(meta class):元类自身,等价于普通 __new__ 的 cls
    # name:将要生成的类名称(字符串)
    # bases:父类元组 (Base1, Base2,...),就是类定义的继承列表
    # namespace:类的命名空间字典,存放方法、类属性 {"__init__":..., "attr":123}
    def __new__(mcs, name, bases, namespace):
        # 从类命名空间 namespace 中获取注解字典 __annotations__
        # 如果 age:int,则 __annotations__ = {'age': int}
        annotations = namespace.get("__annotations__", {})
        for attr_name, attr_type in annotations.items():
            # 以_开头的属性跳过
            if attr_name.startswith("_"):
                continue
            # 为非_开头的属性自动添加 Typed
            namespace[attr_name] = Typed(attr_name, attr_type)
        return super().__new__(mcs, name, bases, namespace)


class User(metaclass=TypedMeta):
    name: str
    age: int
    score: float

    def __init__(self, name: str, age: int, score: float):
        self.name = name
        self.age = age
        self.score = score


if __name__ == "__main__":
    u = User("Alice", 30, 95.5)
    print(u.name, u.age, u.score)
    try:
        u.age = "30"            # TypeError: 属性 age 期望类型 int, 实际得到 str
    except TypeError as e:
        print(e)

元类(Metaclass):控制类的创建

在 Python 中,对象由类创建,类由元类创建。例如:

  • obj = Class() 类创建实例对象

  • Cls = type(...) 元类创建类本身

type 就是 Python 默认元类,所有普通类,顶层元类都是 type。例如:

# 默认元类是 type
class Foo:
    pass
print(type(Foo))          # <class 'type'>

# 显式指定元类为 type(等价于不写)
class Bar(type):
    pass

class Baz(metaclass=type):
    pass
print(type(Baz))          # <class 'type'>

自定义元类:继承 type

自定义元类需要重写 __new__(创建类对象)或 __init__(初始化类对象)。方法定义如下:

  • __new__(mcs, name, bases, namespace):负责创建类对象(分配内存,产出类),参数如下:

    • mcs:元类本身

    • name:待创建的类名(字符串)

    • bases:父类组成的元组

    • namespace:类的命名空间字典,存放属性、方法、注解

  • __init__(cls, name, bases, namespace):类对象已经创建完毕,对类做初始化配置,参数如下:

    • cls:已经创建完成的目标类对象(重点区分元类 mcs)

    • name:类名字符串

    • bases:父类元组

    • namespace:类定义时的原始命名空间字典

例如:

class SingletonMeta(type):
    """单例元类:确保每个类只有一个实例"""
    _instances = {}

    # 执行 类名(参数) 实例化对象时触发:
    # obj = Demo(1,2) → 调用元类的 __call__
    # 参数如下:
    # cls:当前要实例化的类(不是元类,是你 Class() 要创建实例的那个类)
    # *args, **kwargs:实例化时传入的参数 obj = cls(*args, **kwargs)
    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, url: str):
        self.url = url
        print(f"[INIT] Database created with url={url}")

    def query(self, sql: str) -> str:
        return f"Result of: {sql}"

# 测试单例
db1 = Database("postgresql://localhost/db1")
db2 = Database("postgresql://localhost/db2")
print(db1 is db2)         # True
print(db1.url)            # postgresql://localhost/db1(第二次参数被忽略)
print(db2.query("SELECT *"))

运行代码,输出:

[INIT] Database created with url=postgresql://localhost/db1
True
postgresql://localhost/db1
Result of: SELECT *

元类控制类创建过程:自动注册子类

下面利用自定义元类 PluginMeta 实现插件自动注册机制。

元类在类定义阶段的 __new__ 方法拦截类创建过程,通过判断是否存在父类,自动将所有继承自 BasePlugin 的插件子类存入注册表 registry,基类本身不会被注册。

BasePlugin 作为统一插件父类,规范插件接口,要求子类实现 run 方法;UpperPlugin、ReversePlugin、PrefixPlugin 是具体功能插件,仅需继承基类即可完成自动注册,无需手动登记。

该模式适合插件系统、策略处理器开发,新增功能只需新增插件子类,框架自动发现并管理,实现代码解耦与插件即插即用。

完整代码如下:

class PluginMeta(type):
    """自动注册子类的元类"""
    # 注册表:key=类名字符串,value=插件类对象
    registry = {}

    def __new__(mcs, name, bases, namespace):
        """
        元类__new__:创建类对象时触发
        :param mcs: 元类自身 PluginMeta
        :param name: 待创建的类名
        :param bases: 当前类继承的父类元组
        :param namespace: 类命名空间字典
        :return: 新生成的类对象
        """
        # 调用父类元类type,正式生成类
        cls = super().__new__(mcs, name, bases, namespace)
        # bases不为空说明存在父类,代表是子类;过滤最顶层基类(无父类)
        if bases:
            # 将子类存入元类注册表
            mcs.registry[name] = cls
            print(f"[REGISTER] 插件 {name} 已注册")
        return cls


class BasePlugin(metaclass=PluginMeta):
    """所有插件的基类,绑定注册元类"""
    name = ""

    def run(self, data: str) -> str:
        """插件执行入口,子类必须实现"""
        raise NotImplementedError("插件需要重写 run 方法")


class UpperPlugin(BasePlugin):
    """转大写插件"""
    name = "upper"

    def run(self, data: str) -> str:
        return data.upper()


class ReversePlugin(BasePlugin):
    """字符串反转插件"""
    name = "reverse"

    def run(self, data: str) -> str:
        return data[::-1]


class PrefixPlugin(BasePlugin):
    """增加前缀插件"""
    prefix = "[PRE]"

    def run(self, data: str) -> str:
        return f"{self.prefix} {data}"


print("\n已注册插件:")
for name, cls in PluginMeta.registry.items():
    print(f"  - {name}: {cls}")

# 根据注册表动态实例化插件并执行
text = "hello"
for name, cls in PluginMeta.registry.items():
    p = cls()
    print(f"{name}: {p.run(text)}")

运行代码,输出:

[REGISTER] 插件 UpperPlugin 已注册
[REGISTER] 插件 ReversePlugin 已注册
[REGISTER] 插件 PrefixPlugin 已注册

已注册插件:
  - UpperPlugin: <class '__main__.UpperPlugin'>
  - ReversePlugin: <class '__main__.ReversePlugin'>
  - PrefixPlugin: <class '__main__.PrefixPlugin'>
UpperPlugin: HELLO
ReversePlugin: olleh
PrefixPlugin: [PRE] hello

元类修改类属性:强制命名规范

下面代码自定义了元类 NamingMeta,利用元类在类创建阶段拦截类的构建流程,自动校验类内命名规范,过滤保留的 __xxx__魔术方法。

要求所有可调用的方法名称必须是小写蛇形命名(snake_case),普通公有类常量必须全部大写。

当定义类并指定 metaclass=NamingMeta 时,在代码加载、类创建瞬间完成校验,一旦出现不符合命名规则的属性或方法,直接抛出 TypeError,从底层强制统一代码规范,避免命名风格混乱。

完整代码:

class NamingMeta(type):
    """强制属性和方法命名规范:
    - 类公有常量必须全大写
    - 方法必须使用 snake_case 小写蛇形命名
    在类创建阶段自动校验,命名违规直接抛出异常
    """

    def __new__(mcs, name, bases, namespace):
        """
        元类__new__:类对象创建时触发,统一校验类内成员命名
        :param mcs: 元类自身 NamingMeta
        :param name: 待创建的类名称
        :param bases: 父类元组
        :param namespace: 原始类命名空间字典(存放所有属性、方法)
        :return: 校验通过后生成的新类对象
        """
        new_namespace = {}
        # 遍历类中所有属性、方法
        for attr_name, attr_value in namespace.items():
            # 魔术方法(__init__/__str__等)不做命名校验,直接保留
            if attr_name.startswith("__") and attr_name.endswith("__"):
                new_namespace[attr_name] = attr_value
                continue

            # 判断是可调用对象(函数/方法),方法名必须全小写(snake_case)
            if callable(attr_value) and not attr_name.islower():
                raise TypeError(
                    f"类 {name} 的方法 {attr_name} 必须使用 snake_case"
                )

            # 普通公有类常量规则:
            # 不是方法、不以_开头(非私有)、名称不全大写 → 违规
            if (not callable(attr_value) and
                not attr_name.startswith("_") and
                attr_name.upper() != attr_name):
                raise TypeError(
                    f"类 {name} 的类常量 {attr_name} 必须全大写"
                )
                
            new_namespace[attr_name] = attr_value

        # 使用校验完成的命名空间构建类对象
        return super().__new__(mcs, name, bases, new_namespace)


# 绑定命名校验元类,创建类时自动执行命名检查
class Config(metaclass=NamingMeta):
    MAX_SIZE = 100           # 符合规范:公有类常量全大写
    TIMEOUT = 30             # 符合规范

    def __init__(self):
        self.data = []

    def load_data(self):       # 符合规范:方法采用 snake_case
        pass

    def process_item(self):    # 符合规范
        pass


# 取消注释会触发 TypeError,命名不合法示例
# class BadConfig(metaclass=NamingMeta):
#     max_size = 100          # 错误:公有类常量未全大写
#     def LoadData(self):     # 错误:方法名不是小写snake_case
#         pass

print("命名规范检查通过")

综合实战:微型 ORM 框架

该示例结合元类 + 描述符,实现一个支持字段定义、类型校验和 CRUD 的微型 ORM。

Field 作为描述符,负责拦截模型字段的读写。

ModelMeta 自定义元类,在模型类定义阶段自动扫描收集所有 Field 字段、自动生成数据表名、动态注入__init__构造方法,并完成模型全局注册,同时提供内存字典模拟数据库存储。顶层 Model 作为 ORM 基类,封装 create、all、get、filter、save 等通用增查接口。

业务只需继承 Model 并声明 Field 字段即可定义数据模型,无需重复编写样板代码。所有数据保存在内存中,支持基于字段条件的单条查询、批量过滤,完整演示了主流 ORM 底层依靠元类自动解析模型字段的核心原理。

完整代码如下:

from typing import Any, Dict, List, Optional, Type

class Field:
    """ORM 字段描述符
    作为模型类属性,拦截字段的读取与赋值操作,管理实例字段数据存储
    """

    def __init__(self, name: str, field_type: str, primary_key: bool = False):
        # 数据库字段名
        self.name = name
        # 数据库字段类型(INTEGER/VARCHAR/TEXT)
        self.field_type = field_type
        # 是否为主键
        self.primary_key = primary_key

    def __get__(self, instance: Any, owner: Type) -> Any:
        """描述符取值方法
        instance=None:通过类名访问字段,返回Field对象本身
        instance存在:从实例字典读取字段值
        """
        if instance is None:
            return self
        return instance.__dict__.get(self.name)

    def __set__(self, instance: Any, value: Any) -> None:
        """描述符赋值方法:将字段值存入实例自身__dict__"""
        instance.__dict__[self.name] = value


class ModelMeta(type):
    """ORM 自定义元类
    在模型类定义阶段自动扫描Field字段、生成数据表名、注册模型、动态生成构造方法
    """
    # 模型注册表:key=模型类名,value=模型类
    _registry: Dict[str, Type] = {}
    # 内存模拟数据库:key=数据表名,value=数据表内所有记录
    _db: Dict[str, List[Dict]] = {}

    def __new__(mcs, name, bases, namespace):
        """元类__new__:创建模型类对象时触发"""
        # 先构建原始类对象
        cls = super().__new__(mcs, name, bases, namespace)

        # 过滤基类Model,仅处理业务模型子类(User/Post)
        if name == "Model":
            return cls

        # 遍历类命名空间,收集所有Field类型字段
        fields = {}
        for attr_name, attr_value in namespace.items():
            if isinstance(attr_value, Field):
                fields[attr_name] = attr_value
        cls._fields = fields

        # 自动生成表名:类名小写+s 例 User → users
        cls._table_name = name.lower() + "s"
        # 将模型存入全局注册表
        mcs._registry[name] = cls
        # 在内存数据库初始化一张空数据表
        mcs._db[cls._table_name] = []

        # 动态生成 __init__ 构造函数,支持关键字参数初始化字段
        def auto_init(self, **kwargs):
            for field_name, field in fields.items():
                setattr(self, field_name, kwargs.get(field_name))

        cls.__init__ = auto_init
        return cls

    @property
    def db(cls) -> List[Dict]:
        """类属性:快捷获取当前模型对应的内存数据表"""
        return ModelMeta._db.get(cls._table_name, [])


class Model(metaclass=ModelMeta):
    """ORM 模型基类
    所有业务模型继承此类,封装通用数据库CRUD操作(内存版)
    """

    @classmethod
    def create(cls, **kwargs) -> "Model":
        """类方法:新建并保存一条记录,返回模型实例"""
        instance = cls(**kwargs)
        # 提取所有字段构成数据库记录字典
        record = {k: getattr(instance, k) for k in cls._fields}
        cls.db.append(record)
        instance._id = len(cls.db)  # 简易模拟自增主键ID
        return instance

    @classmethod
    def all(cls) -> List[Dict]:
        """查询表内全部数据"""
        return cls.db

    @classmethod
    def get(cls, **filters) -> Optional[Dict]:
        """单条查询:按条件匹配,返回第一条符合条件记录,无结果返回None"""
        for record in cls.db:
            if all(record.get(k) == v for k, v in filters.items()):
                return record
        return None

    @classmethod
    def filter(cls, **filters) -> List[Dict]:
        """条件批量查询,返回所有匹配的记录列表"""
        return [
            r for r in cls.db
            if all(r.get(k) == v for k, v in filters.items())
        ]

    def save(self) -> None:
        """实例方法:将当前实例数据新增存入内存表(简化版仅支持追加,无更新逻辑)"""
        record = {k: getattr(self, k) for k in self._fields}
        self.__class__.db.append(record)

    def __repr__(self) -> str:
        """格式化打印模型实例,方便调试查看字段"""
        attrs = ", ".join(f"{k}={getattr(self, k)!r}" for k in self._fields)
        return f"{self.__class__.__name__}({attrs})"


# 定义业务数据模型,继承Model,使用Field声明数据库字段
class User(Model):
    id = Field("id", "INTEGER", primary_key=True)
    name = Field("name", "VARCHAR")
    email = Field("email", "VARCHAR")
    age = Field("age", "INTEGER")


class Post(Model):
    id = Field("id", "INTEGER", primary_key=True)
    title = Field("title", "VARCHAR")
    content = Field("content", "TEXT")
    user_id = Field("user_id", "INTEGER")


# ORM 使用示例
if __name__ == "__main__":
    # 创建用户记录
    u1 = User.create(name="Alice", email="alice@example.com", age=30)
    u2 = User.create(name="Bob", email="bob@example.com", age=25)
    print("所有用户:", User.all())

    # 单条件查询
    found = User.get(name="Alice")
    print("查询 Alice:", found)

    # 批量条件过滤
    adults = User.filter(age=30)
    print("30岁用户:", adults)

    # 创建文章记录
    p1 = Post.create(title="Hello", content="World", user_id=1)
    print("所有文章:", Post.all())

    # 查看元类自动生成信息
    print("\n注册模型:", list(ModelMeta._registry.keys()))
    print("User 字段:", list(User._fields.keys()))
    print("Post 表名:", Post._table_name)

运行代码,输出:

所有用户: [{'id': None, 'name': 'Alice', 'email': 'alice@example.com', 'age': 30}, {'id': None, 'name': 'Bob', 'email': 'bob@example.com', 'age': 25}]
查询 Alice: {'id': None, 'name': 'Alice', 'email': 'alice@example.com', 'age': 30}
30岁用户: [{'id': None, 'name': 'Alice', 'email': 'alice@example.com', 'age': 30}]
所有文章: [{'id': None, 'title': 'Hello', 'content': 'World', 'user_id': 1}]

注册模型: ['User', 'Post']
User 字段: ['id', 'name', 'email', 'age']
Post 表名: posts

__prepare__ 与自定义命名空间

Python3 的元类支持 __prepare__ 方法,返回一个字典(或自定义映射)用于存储类的命名空间。这是类定义体执行前的钩子。例如:

class OrderedMeta(type):
    """记录类属性定义顺序的元类"""

    @classmethod
    def __prepare__(mcs, name, bases, **kwargs):
        """
        类定义执行前自动调用
        自定义类代码块使用的命名空间对象
        返回 OrderedDict,保证属性写入时保留源码定义顺序
        """
        from collections import OrderedDict
        return OrderedDict()

    def __new__(mcs, name, bases, namespace):
        """
        创建类对象;namespace 就是上面 __prepare__ 返回的有序字典
        将有序字典转为普通dict用于构造类,并把属性顺序存入类属性 _field_order
        """
        cls = super().__new__(mcs, name, bases, dict(namespace))
        # 保存源码中属性、方法自上而下的定义顺序
        cls._field_order = list(namespace.keys())
        return cls


class Schema(metaclass=OrderedMeta):
    # 属性自上而下的顺序会被元类捕获
    name = ""
    age = 0
    email = ""

    def __init__(self):
        pass

# 打印类内成员源码定义顺序
print(Schema._field_order)

运行代码,输出如下:

['__module__', '__qualname__', '__firstlineno__', 'name', 'age', 'email', '__init__', '__static_attributes__']

更多元编程参考官方文档。

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号