Python子类工厂

5

我有许多单一父类的子类,想根据序列化数据中的字符串实例化它们。我的方法是将实例化字符串映射到类,然后使用工厂方法创建相应的对象:

class Superclass:
    @classmethod
    def get_instance(cls, string):
        return CLASSMAP[string]()

    def name(self):
        pass

class Foo(Superclass):
    def name(self):
        return "my name is Foo"

class Bar(Superclass):
    def name(self):
        return "i'm Bar"

class Baz(Superclass):
    def name(self):
        return "i am called Baz"

CLASSMAP = {
    'foo': Foo,
    'bar': Bar,
    'baz': Baz,
}

我不喜欢将CLASSMAP作为全局字典,但是由于子类还没有定义(当然,它们必须在超类之后定义),所以我不能在声明中将其定义为超类的成员。当然,我可以在类之后将类映射分配到类中:

Superclass.CLASSMAP = {...}

可以将超类的 get_instance() 方法引用尚不存在的字段,但这似乎更加笨重且容易出错。

整个模式是否存在更多符合 Python 风格的方法?


CONSTANT 字典对我来说没问题。 - juanpa.arrivillaga
将像CLASSMAP这样的东西封装到另一个原始类中,并编写包装函数以使用私有变量来完成工作,这总是一个好主意! - NoobEditor
你可以使用元类(Metaclass)来将子类注册到类变量中。下面是一个示例,它使用了一个集合(set)来注册子类。很容易将其更改为字典(dict):http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Metaprogramming.html#example-self-registration-of-subclasses - Sebastian Stigler
1个回答

2

如果你让你的超类继承自object,那么你就可以通过cls.__subclasses__()方法来动态地解决这个问题:

class Superclass(object):
    @classmethod
    def get_instance(cls, string):
        return next(c for c in cls.__subclasses__() if c.__name__.lower() == string)()

结合动态添加类和仍然具有O(1)类查找优势的混合方法:

class Superclass(object):
    @classmethod
    def get_instance(cls, string):
        return MAPPING[string]()

class Foo(Superclass):
    lookup = 'foo'

class Bar(Superclass):
    lookup = 'bar'

MAPPING = {c.lookup: c for c in Superclass.__subclasses__()}

类似“SuperClass没有__subclasses__成员”的错误消息。 - Md Mahfuzur Rahman
当然,这是一种可能性,但在这种特定情况下,我宁愿明确地进行映射,而不是将自己承诺以特定方式命名类并动态地篡改名称。 - voland
@schwobaseggl 是的。就像 class Superclass(object) - Md Mahfuzur Rahman
@voland 如果您在序列化时也使用类名,则可以避免所有混淆。如果您更改类的名称,您的序列化数据将无法正常工作,除非您有一个显式的反向映射用于序列化,并且需要保持同步。优缺点都有;)我赞同您的明确性。 - user2390182
在这种情况下,序列化数据由第三方提供,我只需要单向处理数据,所以这不是问题。对于开篇有点不太清楚,我感到抱歉。 - voland
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接