我编写了一个类
BaseRegistry
,它使用类方法作为装饰器,以类属性字典中的字符串名称注册其他类registered
。
此字典用于返回与为其
__new__
方法指定的字符串名称关联的类。
这工作得很好,但现在,我想创建多个此类注册表而不重复代码。当然,如果我继承于
BaseRegistry
,字典将在所有子类之间共享,这不是我想要的。
我不知道如何实现这种“模板化”。
下面是代码示例:
class BaseRegistry:
registered = {}
@classmethod
def add(cls, name):
def decorator(function):
cls.registered[name] = function
return function
return decorator
def __new__(cls, name):
return cls.registered[name]
class RegistryOne(BaseRegistry):
pass
class RegistryTwo(BaseRegistry):
pass
@RegistryOne.add("the_one")
class Example1:
def __init__(self, scale_factor=1):
self.scale_factor = scale_factor
@RegistryOne.add("the_two")
class Example2:
def __init__(self, scale_factor=2):
self.scale_factor = scale_factor
if __name__ == "__main__":
the_one = RegistryOne("the_one")()
print(f"{the_one.scale_factor=}")
assert RegistryOne.registered != RegistryTwo.registered
当然,我想找到一个解决方案来使这段代码正常工作,但我也愿意接受任何可以实现相同目标的替代实现。
编辑:通过此实现,Pylint 抱怨该行:
BaseRegistry
此消息:
E1102:RegistryOne('the_one')不可调用(not-callable)
the_one = RegistryOne("the_one")()
方法,以使用自己的
__init_subclass__
字典初始化每个子类:registered
演示:https://ideone.com/6SaHG3
,如果你真的,真的想要class Registry:
def __init__(self):
self.registered = {}
def add(self, name):
def decorator(function):
self.registered[name] = function
return function
return decorator
def __call__(self, name):
return self.registered[name]
RegistryOne = Registry()
RegistryTwo = Registry()
和
RegistryOne
成为类,那么元类可以解决这个问题:RegistryTwo
然后您可以将其用作:
from functools import partial
class MetaRegistry(type):
def __new__(mcs, name, bases, namespace):
# The default thing
instance = super().__new__(mcs, name, bases, namespace)
# This creates a class-level variable for each class
# similar to how normal classes would create such variables for their instances.
instance.registered = {}
return instance
def __call__(cls, name):
# Instead of creating an instance of `cls` (the default),
# we look for a registered class and return that.
# In case you care, this violates the principle of leash astonishment.
return cls.registered[name]
# No @classmethod here.
def add(cls, name, registry = None):
if registry is None:
return partial(cls.add, name)
cls.registered[name] = registry
return registry
...或:
class RegistryOne(metaclass = MetaRegistry):
pass
剩下的事情完全按计划进行:
class BaseRegistry(metaclass = MetaRegistry):
pass
class RegistryOne(BaseRegistry):
pass
class RegistryTwo(BaseRegistry):
pass
@RegistryOne.add("the_one")
class Example1:
...
@RegistryOne.add("the_two")
class Example2:
...