根据参数选择子类

10 投票
4 回答
13736 浏览
提问于 2025-04-17 01:06

我有一个模块(db.py),它可以从不同类型的数据库(比如sqlite、mysql等)加载数据。这个模块里面有一个叫做db_loader的类,还有一些子类(sqlite_loader、mysql_loader),这些子类都是从db_loader继承过来的。

使用的数据库类型是在一个单独的参数文件里定义的。

那么用户要怎么得到正确的对象呢?

也就是说,我该怎么写:

loader = db.loader()

我是在db.py模块里用一个叫做loader的方法,还是有更优雅的方法,让一个类根据参数自动选择它的子类?有没有什么标准的方法来处理这种情况?

4 个回答

3

我会把子类的名字存储在参数文件里,然后用一个工厂方法来根据这个名字创建相应的类实例:

class loader(object):
  @staticmethod
  def get_loader(name):
    return globals()[name]()

class sqlite_loader(loader): pass

class mysql_loader(loader): pass

print type(loader.get_loader('sqlite_loader'))
print type(loader.get_loader('mysql_loader'))
10

有没有更优雅的方法让一个类根据参数选择自己的子类呢?

你可以通过重写基类的 __new__ 方法来实现这个功能。这样,你只需要写 loader = db_loader(db_type)loader 就会“自动”变成适合数据库类型的正确子类。这个方法比其他答案稍微复杂一点,但在我看来,它绝对是最优雅的。

最简单的形式如下:

class Parent():
    def __new__(cls, feature):
        subclass_map = {subclass.feature: subclass for subclass in cls.__subclasses__()}
        subclass = subclass_map[feature]
        instance = super(Parent, subclass).__new__(subclass)
        return instance

class Child1(Parent):
    feature = 1

class Child2(Parent):
    feature = 2

type(Parent(1))  # <class '__main__.Child1'>
type(Parent(2))  # <class '__main__.Child2'>

(注意,只要 __new__ 返回的是 cls 的实例,实例的 __init__ 方法会自动被调用。)

不过,这个简单的版本有一些问题,需要根据你的需求进行扩展和调整。最明显的是,你可能需要处理以下内容:

Parent(3)  # KeyError
Child1(1)  # KeyError

所以我建议要么把 cls 加入到 subclass_map 中,要么把它作为默认值使用,比如 subclass_map.get(feature, cls)。如果你的基类不打算被实例化——也许它甚至有抽象方法?——那么我建议给 Parent 设置元类 abc.ABCMeta

如果你还有孙子类的话,我建议把收集子类的过程放到一个递归的类方法中,这样可以追溯每个继承关系,添加所有的后代。

在我看来,这个解决方案比工厂方法模式更美观。而且与其他一些答案不同的是,它是自我维护的,因为子类列表是动态创建的,而不是硬编码的映射。而且这个方法只会实例化子类,不像其他一些答案那样,会实例化全局命名空间中与给定参数匹配的任何东西。

15

听起来你想要使用 工厂模式。你需要定义一个工厂方法(可以在你的模块里,或者在一个所有可以生成对象的公共父类中),然后把参数传给这个方法,它就会返回正确类的实例。在Python中,这个问题比维基百科文章中描述的要简单一些,因为你的类型是动态的。

class Animal(object):

    @staticmethod
    def get_animal_which_makes_noise(noise):
        if noise == 'meow':
            return Cat()
        elif noise == 'woof':
            return Dog()

class Cat(Animal):
    ...

class Dog(Animal):
    ...

撰写回答