前置知识: Python

元类与单例模式

00:00
3 min Advanced 2026/6/14

Python元类与单例模式详解。

概述

元类与单例模式是 Python 高级编程中的重要概念。元类控制类的创建过程,单例模式确保一个类只有一个实例。在 Python 中,元类是实现单例模式最优雅的方式之一。本文详细讲解元类的使用方法以及如何用元类和其他方式实现单例模式。

基础概念

元类的作用

元类是创建类的类。当 Python 解释器遇到 class 语句时,会调用来创建类对象。默认的元类是 type,我们可以自定义来拦截和修改的创建过程

单例模式的意义

单例模式确保一个只有一个实例,并提供全局访问。常见应用场景数据库连接配置管理器、日志记录器等需要全局共享状态的场景。

快速上手

元类基本用法

class ValidatedMeta(type):
    """在类创建时验证属性"""
    def __new__(mcs, name, bases, namespace):
        # 验证公开属性必须是简单类型
        for key, value in namespace.items():
            if key.startswith('_'):
                continue
            if not isinstance(value, (int, str, float)):
                raise TypeError(f"{key} must be a simple type")
        return super().__new__(mcs, name, bases, namespace)

class Config(metaclass=ValidatedMeta):
    host = "localhost"
    port = 8080
    # debug = []  # TypeError!

单例模式实现

class SingletonMeta(type):
    """单例元类"""
    _instances = {}

    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):
        self.connection = connect()

详细用法

元类的三个钩子方法

class FullMeta(type):
    """展示元类的三个钩子方法"""

    def __new__(mcs, name, bases, namespace):
        """创建类对象:可以修改类的属性和方法"""
        print(f"1. __new__: 创建类 {name}")
        # 可以在这里添加、删除或修改属性
        namespace['auto_field'] = "由元类自动添加"
        return super().__new__(mcs, name, bases, namespace)

    def __init__(cls, name, bases, namespace):
        """初始化类对象:可以执行额外的初始化逻辑"""
        print(f"2. __init__: 初始化类 {name}")
        super().__init__(name, bases, namespace)

    def __call__(cls, *args, **kwargs):
        """控制实例创建:可以拦截实例化过程"""
        print(f"3. __call__: 创建 {cls.__name__} 的实例")
        instance = super().__call__(*args, **kwargs)
        return instance

class MyClass(metaclass=FullMeta):
    def __init__(self):
        self.value = 42

# 类创建时输出: 1. __new__ 和 2. __init__
obj = MyClass()  # 输出: 3. __call__
print(obj.auto_field)  # "由元类自动添加"

init_subclass 替代简单元类

Python 3.6 引入的 __init_subclass__ 可以替代单元类场景,代码更简洁:

class Plugin:
    """使用 __init_subclass__ 实现自动注册"""
    registry = {}

    def __init_subclass__(cls, name=None, **kwargs):
        super().__init_subclass__(**kwargs)
        Plugin.registry[name or cls.__name__] = cls

class MySQLPlugin(Plugin, name="mysql"):
    pass

class RedisPlugin(Plugin, name="redis"):
    pass

print(Plugin.registry)  # {'mysql': <class 'MySQLPlugin'>, 'redis': <class 'RedisPlugin'>}

单例模式的多种实现

方式一:模块级别变量

# config.py — 最 Pythonic 的单例
class _Config:
    def __init__(self):
        self.debug = False
        self.host = "localhost"

# 模块只会被导入一次,天然单例
config = _Config()

方式二:使用 new

class Singleton:
    _instance = None
    _initialized = False

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        # 注意:__init__ 每次实例化都会被调用
        if not self._initialized:
            self.value = 0
            self._initialized = True

方式三:元类实现

class SingletonMeta(type):
    """线程安全的单例元类"""
    _instances = {}
    _lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            with cls._lock:
                # 双重检查锁定
                if cls not in cls._instances:
                    cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Database(metaclass=SingletonMeta):
    def __init__(self):
        self.connection = "connected"

方式四:装饰器实现

def singleton(cls):
    """单例装饰器"""
    instances = {}

    @functools.wraps(cls)
    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance

@singleton
class Config:
    def __init__(self):
        self.debug = False

config = Config()  # 返回单例实例

单例的继承问题

# 元类实现的单例可以正确处理继承
class SingletonMeta(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        # 每个子类有独立的实例
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

class Base(metaclass=SingletonMeta):
    pass

class ChildA(Base):
    pass

class ChildB(Base):
    pass

a1 = ChildA()
a2 = ChildA()
b1 = ChildB()

print(a1 is a2)  # True
print(a1 is b1)  # False — 不同子类有不同实例

常见场景

场景一:全局配置管理

class ConfigMeta(type):
    """配置管理单例元类"""
    _instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

class Config(metaclass=ConfigMeta):
    def __init__(self):
        self._data = {}

    def get(self, key, default=None):
        return self._data.get(key, default)

    def set(self, key, value):
        self._data[key] = value

    def load_from_file(self, path):
        import json
        with open(path) as f:
            self._data.update(json.load(f))

# 全局使用同一个配置实例
Config().set("debug", True)
print(Config().get("debug"))  # True

场景二:自动注册的工厂

class AutoRegisterMeta(type):
    """自动注册子类的元类"""
    _registry = {}

    def __new__(mcs, name, bases, namespace):
        cls = super().__new__(mcs, name, bases, namespace)
        # 不注册基类
        if name != 'BaseHandler':
            key = namespace.get('handler_name', name.lower())
            mcs._registry[key] = cls
        return cls

class BaseHandler(metaclass=AutoRegisterMeta):
    def handle(self, data):
        raise NotImplementedError

class CreateUserHandler(BaseHandler):
    handler_name = "create_user"
    def handle(self, data):
        return f"创建用户: {data}"

class DeleteUserHandler(BaseHandler):
    handler_name = "delete_user"
    def handle(self, data):
        return f"删除用户: {data}"

# 自动注册
handler = AutoRegisterMeta._registry["create_user"]()
print(handler.handle("Alice"))  # 创建用户: Alice

注意事项

  • 元类代码复杂,优先考虑 __init_subclass__装饰器或普通继承
  • 单例模式使代码难以测试,因为状态测试共享测试时需要提供重置机制
  • 多线程环境下,单例实现需要加保证线程安全
  • 单例违反了单一职责原则,过使用会导致全局状态难以管理
  • Python 的模块导入机制天然保证模块对象单例性,这是最简单实现方式

进阶用法

元类与描述符配合实现 ORM

class Column:
    """数据库列描述符"""
    def __init__(self, col_type, primary_key=False):
        self.col_type = col_type
        self.primary_key = primary_key

    def __set_name__(self, owner, name):
        self.name = name

class ORMMeta(type):
    """ORM 元类:收集列信息"""
    def __new__(mcs, name, bases, namespace):
        columns = {}
        for key, value in namespace.items():
            if isinstance(value, Column):
                columns[key] = value
        namespace['_columns'] = columns
        namespace['_table_name'] = name.lower()
        return super().__new__(mcs, name, bases, namespace)

class Model(metaclass=ORMMeta):
    def save(self):
        cols = ", ".join(self._columns.keys())
        vals = ", ".join(f"'{getattr(self, k)}'" for k in self._columns)
        return f"INSERT INTO {self._table_name} ({cols}) VALUES ({vals})"

class User(Model):
    id = Column(int, primary_key=True)
    name = Column(str)
    email = Column(str)

u = User()
u.id = 1
u.name = "Alice"
u.email = "alice@example.com"
print(u.save())  # INSERT INTO user (id, name, email) VALUES ('1', 'Alice', 'alice@example.com')

可销毁的单例

class DestroyableSingleton(type):
    """可销毁的单例元类"""
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super().__call__(*args, **kwargs)
        return cls._instances[cls]

    def destroy(cls):
        """销毁单例实例(测试用)"""
        if cls in cls._instances:
            del cls._instances[cls]

class Service(metaclass=DestroyableSingleton):
    def __init__(self):
        self.started = True

# 测试时可以重置
def test_service():
    Service.destroy()  # 销毁旧实例
    svc = Service()    # 创建新实例
    assert svc.started

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式