Python父类子类实例化:基于序列化字符串的工厂模式优化问询
这个场景太常见了,全局映射字典确实容易变得杂乱,而且手动维护很容易出错。给你分享几个更符合Python风格的解决方案,完美避开全局变量的问题:
方案1:用类装饰器实现自动注册
这个方案直观易懂,让每个子类通过装饰器主动“注册”到父类的内部映射中,不用手动维护字典:
class Superclass: # 用父类内部的字典代替全局的CLASSMAP _class_map = {} @classmethod def register(cls, key=None): # 支持自定义映射key,默认用子类名的小写 def decorator(subclass): map_key = key if key else subclass.__name__.lower() cls._class_map[map_key] = subclass return subclass return decorator @classmethod def get_instance(cls, string): return cls._class_map[string]() def name(self): pass # 子类通过装饰器注册,可自定义key或用默认规则 @Superclass.register() class Foo(Superclass): def name(self): return "my name is Foo" @Superclass.register(key="custom_bar") class Bar(Superclass): def name(self): return "i'm Bar" @Superclass.register() class Baz(Superclass): def name(self): return "i am called Baz"
这样一来,子类定义时就完成了注册,完全不用再手动更新映射表,而且可以灵活自定义每个子类对应的字符串key。
方案2:用元类实现全自动注册
如果你想要更“无感”的注册方式,可以用元类——它会在子类创建的瞬间自动完成注册,连装饰器都不用加:
class SuperMeta(type): def __new__(meta, class_name, bases, attrs): # 创建子类对象 subclass = super().__new__(meta, class_name, bases, attrs) # 只注册Superclass的子类,排除父类本身 if bases != (object,): # 默认用类名小写作为key,也可以自定义规则 subclass._class_map[class_name.lower()] = subclass return subclass class Superclass(metaclass=SuperMeta): _class_map = {} @classmethod def get_instance(cls, string): return cls._class_map[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"
元类的方式更自动化,但稍微有点抽象。如果你的子类数量多、注册规则统一,这个方案会非常省心。
方案3:动态遍历子类(不推荐但可了解)
Python的类自带__subclasses__()方法,可以返回所有直接子类的列表。你可以在get_instance里动态匹配:
class Superclass: @classmethod def get_instance(cls, string): for subclass in cls.__subclasses__(): if subclass.__name__.lower() == string: return subclass() raise ValueError(f"No subclass found for '{string}'") def name(self): pass # 子类正常定义 class Foo(Superclass): def name(self): return "my name is Foo"
不过这个方案有明显缺点:多层继承时只能拿到直接子类、每次调用都要遍历效率低、无法自定义映射key,所以只适合子类极少且映射规则固定的场景。
内容的提问来源于stack exchange,提问作者voland
相关产品推荐
相关产品推荐

