类也是对象,所以类也有"类"
class Player:
pass
print(type(Player)) # <class 'type'>
Player 这个类,它的"类"是 type。type 就是默认的元类。
type 的另一面:动态创建类
type(name, bases, dict) 可以直接创建一个类:
Player = type("Player", (), {"hp": 100})
p = Player()
print(p.hp) # 100
等价于:
class Player:
hp = 100
理解这点:class 关键字背后就是在调用 type()。
自定义元类
继承 type,重写 __new__ 或 __init__:
class LowerNameMeta(type):
def __new__(mcs, name, bases, attrs):
# 把所有方法名改小写
new_attrs = {
(k.lower() if not k.startswith("_") else k): v
for k, v in attrs.items()
}
return super().__new__(mcs, name, bases, new_attrs)
class Player(metaclass=LowerNameMeta):
def Speak(self): print("hi")
def WALK(self): print("walking")
p = Player()
p.speak() # hi (原来的 Speak 已被改成 speak)
p.walk()
用元类做"自动注册"
最常见的元类应用:类一被定义就自动注册到某个表:
HANDLERS = {}
class HandlerMeta(type):
def __init__(cls, name, bases, attrs):
super().__init__(name, bases, attrs)
if hasattr(cls, "command"):
HANDLERS[cls.command] = cls
class Handler(metaclass=HandlerMeta):
pass
class AddHandler(Handler):
command = "add"
def run(self, *args): print("adding", args)
class ListHandler(Handler):
command = "list"
def run(self, *args): print("listing")
# 不需要手动 register,定义类即注册
print(HANDLERS)
# {'add': <class 'AddHandler'>, 'list': <class 'ListHandler'>}
init_subclass:90% 场景的元类替代品
Python 3.6 引入的 __init_subclass__ 比元类简单多了:
HANDLERS = {}
class Handler:
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if hasattr(cls, "command"):
HANDLERS[cls.command] = cls
class AddHandler(Handler):
command = "add"
效果完全一样,但不用写元类。
何时真的需要元类
- 写框架,要在 class 创建时改属性 / 注入方法 / 校验定义
- ORM(SQLAlchemy)、ABC、Django Model 内部都用元类
写应用代码 99% 用不到元类。看到了能读懂就行。
"Magic 程度排行"
| 工具 | 复杂度 | 何时用 |
|---|---|---|
| 函数 / 装饰器 | 低 | 90% |
| 类装饰器 | 中 | 想给类批量加属性 |
__init_subclass__ |
中 | 想在子类创建时做事 |
| 描述符 | 高 | 想批量复用属性逻辑 |
| 元类 | 最高 | 写框架,无可替代时 |
下一篇讲类型注解 typing——给代码加类型说明。