根据参数选择一个子类

时间:2011-09-01 16:44:08

标签: python oop

我有一个模块(db.py),它从不同的数据库类型(sqlite,mysql等)加载数据。该模块包含一个类db_loader和继承自它的子类(sqlite_loader,mysql_loader)。

正在使用的数据库类型位于单独的params文件中,

用户如何获得正确的对象?

即我该怎么做:

loader = db.loader()

我是否在db.py模块中使用一个名为loader的方法,或者是否有一种更优雅的方法,即类可以根据参数选择自己的子类?有没有标准的方法来做这种事情?

4 个答案:

答案 0 :(得分:6)

听起来你想要Factory Pattern。您可以定义一个工厂方法(在您的模块中,或者可能在它可以生成的所有对象的公共父类中),并将参数传递给它,它将返回正确类的实例。在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):
    ...

答案 1 :(得分:2)

我将子类的名称存储在params文件中,并且有一个工厂方法,可以根据其名称实例化该类:

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'))

答案 2 :(得分:2)

将类存储在dict中,根据您的参数实例化正确的类:

db_loaders = dict(sqlite=sqlite_loader, mysql=mysql_loader)
loader = db_loaders.get(db_type, default_loader)()

其中db_type是您要启用的参数,sqlite_loadermysql_loader是“加载程序”类。

答案 3 :(得分:1)

是否有一种更优雅的方式可以使类根据参数选择自己的子类?

您可以通过重写基类的__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

如果您也有孙子类,那么我建议将子类的集合放到一个递归类方法中,该方法将每个谱系都放在末尾,并添加所有后代。

此解决方案比工厂方法模式恕我直言更漂亮。与其他答案不同,它是自维护的,因为子类列表是动态创建的,而不是保存在硬编码的映射中。而且,这只会实例化子类,这与其他答案之一不同,后者会实例化全局命名空间中与给定参数匹配的任何内容。