Python自定义元类
自定义元类通过继承type并重写__new__或__init__,可在类创建时注入自定义逻辑。
元类基础结构
Python
class CustomMeta(type):
"自定义元类模板"
def __new__(mcs, name, bases, namespace):
"创建类对象"
print(f"CustomMeta.__new__: {name}")
# 可在此修改 namespace、bases 等
cls = super().__new__(mcs, name, bases, namespace)
return cls
def __init__(cls, name, bases, namespace):
"初始化类对象"
print(f"CustomMeta.__init__: {name}")
super().__init__(name, bases, namespace)
def __call__(cls, *args, **kwargs):
"控制实例创建"
print(f"CustomMeta.__call__: {cls.__name__}")
return super().__call__(*args, **kwargs)
class MyClass(metaclass=CustomMeta):
pass
obj = MyClass()
# 输出顺序:
# CustomMeta.__new__: MyClass
# CustomMeta.__init__: MyClass
# CustomMeta.__call__: MyClass(实例创建时)
元类参数顺序
Python
# __new__ 和 __init__ 的参数说明
class DetailedMeta(type):
def __new__(mcs, name, bases, namespace, **kwargs):
"
mcs: 元类本身(CustomMeta)
name: 类名(字符串)
bases: 基类元组
namespace: 类属性字典
kwargs: 类定义时传入的额外参数(如 class Foo(metaclass=Meta, arg=1))
"
print(f"元类: {mcs.__name__}")
print(f"类名: {name}")
print(f"基类: {[b.__name__ for b in bases]}")
print(f"属性: {list(namespace.keys())[:5]}")
print(f"额外参数: {kwargs}")
# kwargs 通常传递给 __init__
cls = super().__new__(mcs, name, bases, namespace)
cls._meta_args = kwargs # 存储额外参数
return cls
def __init__(cls, name, bases, namespace, **kwargs):
super().__init__(name, bases, namespace)
class Example(metaclass=DetailedMeta, version="1.0", debug=True):
attr = 123
属性自动处理
Python
class UppercaseMeta(type):
"自动将属性名转为大写"
def __new__(mcs, name, bases, namespace):
new_namespace = {}
for key, value in namespace.items():
if key.startswith('__') and key.endswith('__'):
new_namespace[key] = value # 保留特殊属性
elif callable(value):
new_namespace[key] = value # 保留方法
else:
new_namespace[key.upper()] = value # 属性大写
return super().__new__(mcs, name, bases, new_namespace)
class Config(metaclass=UppercaseMeta):
host = "localhost"
port = 8080
debug = True
print(Config.HOST) # localhost
print(Config.PORT) # 8080
print(Config.DEBUG) # True
# Config.host # AttributeError
Python
class TypedMeta(type):
"属性类型检查"
def __new__(mcs, name, bases, namespace):
# 检查 _types 定义的类型约束
if '_types' in namespace:
types_spec = namespace['_types']
for attr_name, expected_type in types_spec.items():
if attr_name in namespace:
actual_value = namespace[attr_name]
if not isinstance(actual_value, expected_type):
raise TypeError(
f"{name}.{attr_name} 应为 {expected_type.__name__}, "
f"实际为 {type(actual_value).__name__}"
)
return super().__new__(mcs, name, bases, namespace)
class TypedClass(metaclass=TypedMeta):
_types = {
'name': str,
'count': int,
}
name = "valid"
count = 10
# name = 123 会触发 TypeError
自动注册模式
Python
class RegistryMeta(type):
"自动注册所有子类"
_registry = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
# 注册类(排除基类)
if bases: # 非顶层基类
mcs._registry[name] = cls
return cls
@classmethod
def get_registry(mcs):
return dict(mcs._registry)
class PluginBase(metaclass=RegistryMeta):
"插件基类"
class PluginA(PluginBase):
name = "plugin_a"
class PluginB(PluginBase):
name = "plugin_b"
print(RegistryMeta.get_registry())
# {'PluginA': <class PluginA>, 'PluginB': <class PluginB>}
Python
# 按类型注册
class HandlerRegistry(type):
handlers = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if 'handler_type' in namespace:
handler_type = namespace['handler_type']
mcs.handlers[handler_type] = cls
return cls
class Handler(type):
pass
class TextHandler(metaclass=HandlerRegistry):
handler_type = 'text'
def process(self, data):
return data.strip()
class ImageHandler(metaclass=HandlerRegistry):
handler_type = 'image'
def process(self, data):
return f"processed: {data}"
print(HandlerRegistry.handlers)
# {'text': TextHandler, 'image': ImageHandler}
强制接口实现
Python
class InterfaceMeta(type):
"强制子类实现指定方法"
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
# 检查是否定义了 _required_methods
if hasattr(cls, '_required_methods'):
required = cls._required_methods
# 基类不需要检查
if name != 'Interface':
missing = []
for method in required:
if method not in namespace:
# 检查是否从基类继承
if not any(hasattr(b, method) for b in bases):
missing.append(method)
if missing:
raise NotImplementedError(
f"{name} 必须实现方法: {missing}"
)
return cls
class Interface(metaclass=InterfaceMeta):
_required_methods = ['process', 'validate']
class ValidImpl(Interface):
def process(self):
pass
def validate(self):
pass
# class InvalidImpl(Interface):
# def process(self):
# pass
# NotImplementedError: 必须实现 validate
控制实例创建
Python
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 Singleton(metaclass=SingletonMeta):
def __init__(self, value):
self.value = value
s1 = Singleton(1)
s2 = Singleton(2)
print(s1.value) # 1(第一次创建)
print(s2.value) # 1(同一实例)
print(s1 is s2) # True
Python
class NoInstanceMeta(type):
"禁止实例化的元类"
def __call__(cls, *args, **kwargs):
raise TypeError(f"{cls.__name__} 不允许实例化")
class AbstractClass(metaclass=NoInstanceMeta):
"抽象类,仅作为接口"
# AbstractClass() # TypeError
prepare 自定义命名空间
Python
class OrderedDictMeta(type):
"使用有序字典作为命名空间"
@classmethod
def __prepare__(mcs, name, bases):
return {} # Python 3.6+ 默认字典保持顺序
def __new__(mcs, name, bases, namespace):
# 查看属性定义顺序
order = [k for k in namespace if not k.startswith('__')]
cls = super().__new__(mcs, name, bases, namespace)
cls._attribute_order = order
return cls
class OrderedClass(metaclass=OrderedDictMeta):
first = 1
second = 2
third = 3
print(OrderedClass._attribute_order) # ['first', 'second', 'third']
多元类冲突
Python
class MetaA(type):
pass
class MetaB(type):
pass
class ClassA(metaclass=MetaA):
pass
class ClassB(metaclass=MetaB):
pass
# 多继承时的元类冲突
# class Conflict(ClassA, ClassB): # TypeError: 元类冲突
# 解决方案1:统一元类
class UnifiedMeta(MetaA, MetaB):
pass
class Unified(ClassA, ClassB, metaclass=UnifiedMeta):
pass
# 解决方案2:显式指定元类
class Explicit(ClassA, ClassB, metaclass=MetaA):
pass # 继承MetaA(需兼容MetaB)
要点总结
- **
__new__**创建类对象,可修改属性、基类 - **
__init__**初始化类对象,可添加额外逻辑 - **
__call__**控制实例创建,可实现单例模式 - **
__prepare__**自定义命名空间容器 - 多继承时需解决元类冲突,确保统一元类
📝 发现内容有误?点击此处直接编辑