场景:寻找元类替代品

Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).
-- Tim Peters

元类是 Python 中的一种特殊对象,它控制着类的创建行为,就像普通类控制着实例的创建行为一样。type 是 Python 中最基本的元类,直接调用 type() 就可以创建一个类:

1
2
3
>>> Foo = type('Foo', (), {'bar': 3})
>>> Foo
<class '__main__.Foo'>

在调用 type() 创建类时,需要提供三个参数,它们的含义如下:

  • name: str,需要创建的类名;
  • bases: Tuple[Type],包含其他类的元组,代表类的所有基类;
  • attrs: Dict[str, Any],包含所有类成员(属性、方法)的字典;

元类的功能相当强大,它不光可以修改类,还能修改类的实例。同时它也相当复杂,通常类说,除非要开发一些框架类工具,否则你在日常工作中根本不需要用到元类:

1
2
3
4
5
6
7
8
9
10
11
12
13
_validators = {}

class ValidatorMeta(type):
def __new__(mcs, name, bases, attrs):
ret = super().__new__(mcs, name, bases, attrs)
_validators[attrs['name']] = ret
return ret

class StringValidator(metaclass=ValidatorMeta):
name = 'string'

class IntegerValidator(metaclass=ValidatorMeta):
name = 'int'

类装饰器

虽然类装饰器并不能覆盖元类的所有功能,但在许多场景下,类装饰器可能比元类更适合,因为它不光写起来容易,理解起来也更简单

1
2
3
4
5
6
7
8
9
10
11
12
13
_validators = {}

def register(cls):
_validators[cls.name] = cls
return cls

@register
class StringValidator:
name = 'string'

@register
class IntegerValidator:
name = 'int'

除了上面的注册功能以外,你还可以用类装饰器完成许多实用的事情,比如实现单例设计模式、自动为类追加方法等等。

__init_subclass__ 钩子方法

__init_subclass__ 是类的一个特殊钩子方法,它的主要功能是在类派生出子类时,触发额外的操作。假如某个类实现了这个钩子方法,那么当其他类继承该类时,钩子方法就会被触发:

1
2
3
4
5
6
7
8
9
10
11
12
_validators = {}

class Validator:
def __init_subclass__(cls, **kwargs):
print('{} registered, extra kwargs: {}'.format(cls.__name__, kwargs))
_validators[cls.__name__] = cls

class StringValidator(Validator, foo='bar'):
name = 'string'

class IntegerValidator(Validator):
name = 'int'

__init_subclass__ 非常适合在这种需要触达所有子类的场景中使用,并且同元类相比,钩子方法只要求使用者了解继承,不用掌握更高深的元类相关知识,门槛低了不少。