如何“模板化”使用 __new__ 作为工厂的注册表类?

问题描述 投票:0回答:3

我编写了一个类

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)

python metaclass
3个回答
0
投票

the_one = RegistryOne("the_one")()

 方法,以使用自己的 
__init_subclass__ 字典初始化每个子类:
registered

演示:
https://ideone.com/6SaHG3


0
投票
class BaseRegistry: ... def __init_subclass__(cls): cls.registered = {}

RegistryOne
真正代表基类的实例时,没有充分的理由将它们作为子类。
让它们成为实际实例:

RegistryTwo

演示:
https://ideone.com/LPi1Du


0
投票
这个答案

,如果你真的,真的想要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:
  ...
© www.soinside.com 2019 - 2024. All rights reserved.