类的创建过程
使用 class 关键字定义类时,Python 底层会调用元类来创建类:
class Dog:
pass
# 等效于
class Dog(metaclass=type):
pass
# 等效于
Dog = type("Dog", (), {})
# 等效于
Dog = type.__call__(type, "Dog", (), {})
也就是说,class 关键字只是语法糖,底层仍然是通过 type() 创建类。
自定义元类
通过继承 type,可以自定义元类,控制类的创建过程。
class MyMeta(type):
def __new__(mcs, name, bases, namespace):
print(f"正在创建类: {name}")
print(f"父类: {bases}")
print(f"属性: {list(namespace.keys())}")
# 必须调用 type.__new__ 来真正创建类
cls = super().__new__(mcs, name, bases, namespace)
return cls
# 使用 metaclass 参数指定元类
class Dog(metaclass=MyMeta):
species = "Canis familiaris"
def bark(self):
print("Woof!")
# 等效于
# def bark(self):
# print("Woof!")
# Dog = type.__call__(MyMeta, "Dog", (), {"species": "Canis familiaris", "bark": bark})
# 输出:
# 正在创建类: Dog
# 父类: ()
# 属性: ['__module__', '__qualname__', 'species', 'bark']
参数说明:
mcs:元类自身(类似类方法中的cls)name:类名字符串bases:父类元组namespace:类属性的字典
深入:元类的查找顺序
子类会继承父类的元类:
class MyMeta(type):
pass
class Base(metaclass=MyMeta):
pass
class Child(Base): # 自动继承 MyMeta
pass
print(type(Child)) # <class '__main__.MyMeta'>
如果父类元类不兼容,需要使用更通用的元类:
class MetaA(type):
pass
class MetaB(type):
pass
class A(metaclass=MetaA):
pass
class B(metaclass=MetaB):
pass
# class C(A, B): pass # TypeError! 元类冲突
# 解决方法:创建兼容的元类
class CommonMeta(MetaA, MetaB):
pass
class C(A, B, metaclass=CommonMeta): # 正常
pass
总结
| 概念 | 说明 |
|---|---|
| 元类 | 创建类的类,默认是 type |
__new__ | 创建类,返回类对象 |
__init__ | 初始化类,无返回值 |
__call__ | 控制类的实例化过程 |
| 应用场景 | 命名检查、自动注册、方法增强、ORM 等 |
一、实现单例元类
编写一个元类 SingletonMeta,使得任何使用该元类的类都自动成为单例模式:
class SingletonMeta(type):
_instance = {}
def __call__(cls, *args, **kwds):
if cls not in cls._instance:
ins = super().__call__(*args, **kwds)
cls._instance[cls] = ins
return cls._instance[cls]
pass
class Database(metaclass=SingletonMeta):
def __init__(self, host):
self.host = host
db1 = Database("localhost")
db2 = Database("remote")
print(db1 is db2) # 应该输出 True
print(db1.host) # 应该输出 localhost
class Player(metaclass=SingletonMeta):
pass
p1 = Player()
p2 = Player()
print(p1 is p2)
二、自动注册子类
编写一个元类 PluginMeta,使得任何继承自 Plugin 的子类都会被自动注册到 PluginMeta.registry 字典中(键为类名,值为类本身):
class PluginMeta(type):
registry = {}
def __new__(mcs, name, bases, namespace):
cls = super().__new__(mcs, name, bases, namespace)
if name != "Plugin":
mcs.registry[name] = cls
return cls
pass
class Plugin(metaclass=PluginMeta):
pass
class ImagePlugin(Plugin):
pass
class TextPlugin(Plugin):
pass
class VideoPlugin(Plugin):
pass
print(PluginMeta.registry)
# 应该输出类似:{'ImagePlugin': <class '__main__.ImagePlugin'>, 'TextPlugin': <class '__main__.TextPlugin'>}
三、为所有方法添加日志
编写一个元类 LogMeta,自动为类中每个非私有方法(即不以 _ 开头的方法)添加执行日志。调用方法时,先打印 [LOG] 调用 {方法名},再执行原方法:
class LogMeta(type):
def __new__(mcs, name, bases, namespace):
for key, value in namespace.items():
if not key.startswith("_") and callable(value):
namespace[key] = mcs.log_wrapper(value)
return super().__new__(mcs, name, bases, namespace)
@staticmethod
def log_wrapper(func):
def wrapper(*args, **kwargs):
print(f"[LOG] 调用 {func.__name__}")
r = func(*args, **kwargs)
return r
return wrapper
class Calculator(metaclass=LogMeta):
a = 1
def add(self, a, b):
return a + b
def sub(self, a, b):
return a - b
calc = Calculator()
print(calc.add(3, 5))
print(calc.sub(10, 4))
# 应该输出:
# [LOG] 调用 add
# 8
# [LOG] 调用 sub
# 6




