什么是注册机制
注册机制的核心是注册表 REGISTRY。这个注册表本质是一个字典结构,存储名称到类/函数的映射。这样,我们就可以用配置文件中不同的字段切换不同的类实现或者函数实现,增加项目的兼容性和易用性。
举一个最简单的例子,假设动物园有很多动物,每种动物都有不同的活动习性。那么动物园每添加一种新的动物,我们就需要实现一个新的“类”。为了知道动物园一共有多少种动物,并且某种动物的习性是什么,就会用到注册表 REGISTRY, 来存储这种动物名称到“类”的映射。
注册机制的实现
以上我们理解了什么是注册机制,那么最简的注册机制的实现就是创建一个字典。然后每次对项目有改动,比如增加一个新的类的时候,手动修改这个字典。但是这种有一个缺陷。第一个是这种字典往往是全局变量,项目组中可能某个不懂事儿的家伙,为了走捷径实现某个功能把注册表中一些东西删除/修改了,这就会造成其他功能的崩溃。另一个是每次添加一个新的类都需要手动修改的话会很麻烦。
那么有没有稍微好一些的实现方法呢?本文抛转引玉提出一个简洁的实现方法。具体来说,就是通过python中的装饰器,来自动化地更新注册表。
首先看 registry.py 代码,这里实现了一个Register类。
class Register(object):
def __init__(self, name):
self.module_dict = dict()
self.name = name
def register_module(self):
def _register(target):
name = target.__name__.lower()
self.module_dict[name] = target
return target
return _register
def modules(self):
return dict(self.module_dict)
比较重要的是 register_module这个函数。其功能在于把要注册的类或者对象,函数等自动添加到 self.module_dict (注册表)这个字典里面。如果需要使用,就调用 modules 返回注册表。
实现好Register后,我们可以用其注册一些类,比如models.py中的代码。
from registry import Register
MODELS = Register("MODELS")
@MODELS.register_module()
class Bird:
def __init__(self, extra=''):
print("Get Bird, " + extra)
@MODELS.register_module()
class Tiger:
def __init__(self, extra=''):
print("Got Tiger," + extra)
我们先定义了一个名字为 MODELS的注册表实例,然后利用MODELS将Bird和Tiger都添加到它的注册表中。
那么如何使用MODELS注册表呢,我们提供了一个例子,如 main.py 中
from models import MODELS
config = {
'class_name': 'tiger',
'params': {
'extra': 'hello'
},
}
def build_from_config(cfg, REGISTRY):
name = config['class_name']
params = config['params']
for key, target in REGISTRY.modules().items():
if key == name:
return target(**params)
return None
if __name__ == "__main__":
build_from_config(config, MODELS)
首先,我们需要导入注册表 MODELS,和配置数据 config。主要的功能实现是 build_from_config, 这个函数利用配置数据和注册表来构建我们需要的实例。运行以上文件,我们可以得到输出
Got Tiger,hello
这说明,我们成功创建了Tiger实例。
注册机制的劣势和好处
通过以上实现,我们很清楚注册机制的优势,就是使用起来很简单,只需要提供配置文件即可,系统的灵活性很高。但是代价主要被转移到了开发者这边,在开发的时候,由于注册表屏蔽了原始的类定义,编码的时候一些自动补全,方法联想就没法用了。还有就是在debug的时候,程序跳转不好确定,断点也不是很好打。