跳到主要内容

元类

问题

什么是元类?typemetaclass 的关系是什么?元类有哪些实际应用?

答案

类也是对象

在 Python 中,类本身就是对象,它由元类创建。默认元类是 type

class MyClass:
pass

print(type(MyClass)) # <class 'type'>
print(type(type)) # <class 'type'> — type 自身的类型也是 type
print(isinstance(MyClass, type)) # True

所以关系链是:type → 创建 → class → 创建 → instance

type 动态创建类

# 常规方式
class Dog:
sound = "汪汪"
def speak(self):
return self.sound

# 等价的 type() 方式
Dog = type("Dog", (object,), {
"sound": "汪汪",
"speak": lambda self: self.sound,
})

d = Dog()
print(d.speak()) # 汪汪

type(name, bases, attrs) 三个参数:类名、基类元组、属性字典。

自定义元类

class SingletonMeta(type):
"""元类实现单例模式"""
_instances = {}

def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
# 调用 type.__call__ 创建实例
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]

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

db1 = Database()
db2 = Database()
print(db1 is db2) # True

__init_subclass__ — 元类的轻量替代

Python 3.6+ 提供了 __init_subclass__,大多数场景下可以替代元类:

class Plugin:
_registry = {}

def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
Plugin._registry[cls.__name__] = cls

class AudioPlugin(Plugin): pass
class VideoPlugin(Plugin): pass

print(Plugin._registry)
# {'AudioPlugin': <class 'AudioPlugin'>, 'VideoPlugin': <class 'VideoPlugin'>}
优先使用 __init_subclass__

元类强大但复杂,90% 的场景用 __init_subclass__ 或装饰器更简单。只在需要控制类创建过程本身时才用元类。


常见面试问题

Q1: __new__ 在元类中的作用?

答案

元类的 __new__类(不是实例)被创建时调用,可以修改类的属性和行为:

class UpperAttrMeta(type):
def __new__(mcs, name, bases, attrs):
# 将所有非魔术方法的属性名转为大写
upper_attrs = {}
for key, val in attrs.items():
if not key.startswith("__"):
upper_attrs[key.upper()] = val
else:
upper_attrs[key] = val
return super().__new__(mcs, name, bases, upper_attrs)

class Foo(metaclass=UpperAttrMeta):
bar = "hello"

print(hasattr(Foo, "bar")) # False
print(hasattr(Foo, "BAR")) # True
print(Foo.BAR) # "hello"

Q2: ABCMeta 是什么?

答案

ABCMetaabc 模块提供的元类,用于定义抽象基类:

from abc import ABC, abstractmethod

# ABC 等价于 metaclass=ABCMeta
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...

# Shape() → TypeError

Q3: 什么时候用元类?什么时候用装饰器?

答案

场景推荐方案
修改类属性/方法装饰器或 __init_subclass__
注册子类__init_subclass__
验证类定义__init_subclass__
控制类的创建过程元类
ORM 字段映射元类(如 Django Model)
单例元类或装饰器

相关链接