在运行时替换python Mixin类

时间:2017-12-11 13:19:10

标签: python python-2.7 mixins

我有一个数据访问类,用于从数据库中获取数据。它是作为一个mixin实现的,即ReferenceData我希望用MockReferenceData类交换这个类,所以在单元测试中我不必访问DB。但是没有用。

*注意:我不能使用依赖注入。因为团队不想使用它,因为它将是非常大的代码更改。所以我希望在运行时替换mixin。

class MockReferenceData(object):
  def dbName(self):
    return 'mock'

  def totalNumberOfSeats(self):
    return '10'

class ReferenceData(object):
  def dbName(self):
    return 'real DB'

  def totalNumberOfSeats(self):
    return 'Fetch from DB'

class Car(ReferenceData):
  def showNumberOfSeats(self):
    print self.totalNumberOfSeats()


class Train(ReferenceData):
  def showNumberOfSeats(self):
    print self.totalNumberOfSeats()

c = Car()
c.showNumberOfSeats()
t = Train()
t.showNumberOfSeats()

def extend_instance(obj, cls):
    """Apply mixins to a class instance after creation"""
    base_cls = obj.__class__
    base_cls_name = obj.__class__.__name__
    obj.__class__ = type(base_cls_name, (base_cls, cls),{})

extend_instance(c, MockReferenceData)
c.showNumberOfSeats() // output now should be 10

输出是:

Fetch from DB
Fetch from DB
Fetch from DB

我希望因为我使用 extend_instance 方法指向新的模拟类输出将是:

Fetch from DB
Fetch from DB
10

3 个答案:

答案 0 :(得分:1)

如果您查看班级Car的{​​{3}},可以看到:

>>> Car.__mro__
(<class '__main__.Car'>, <class '__main__.ReferenceData'>, <type 'object'>)

首先在Car上查找方法,然后在ReferenceData上查找方法,最后在object上查找。

将此与新课程的MRO进行比较(为清晰起见,我使用NewCar作为名称):

>>> type('NewCar', (Car, MockReferenceData), {}).__mro__
(<class '__main__.NewCar'>, <class '__main__.Car'>, <class '__main__.ReferenceData'>, <class '__main__.MockReferenceData'>, <type 'object'>)

这包含Car类的MRO。此处的方法仍会先在Car上查找,然后在ReferenceData上查找,以便在totalNumberOfSeats上找到ReferenceData时,不会使用来自MockReferenceData的实施。< / p>

您可以做的是在 Car类之前将模拟类插入MRO

>>> type('NewCar', (MockReferenceData, Car), {}).__mro__
(<class '__main__.NewCar'>, <class '__main__.MockReferenceData'>, <class '__main__.Car'>, <class '__main__.ReferenceData'>, <type 'object'>)

现在首先在MockReferenceData类中查找方法,如果它们不存在,则会回退到之前的版本。所以这个extend_instance方法适用于这个简单的情况:

def extend_instance(obj, cls):
    """Apply mixins to a class instance after creation"""
    obj.__class__ = type(obj.__class__.__name__, (cls, obj.__class__),{})

答案 1 :(得分:1)

谢谢@mata我也做了同样的改变了订单而且工作正常。

def extend_instance(obj, cls):
    """Apply mixins to a class instance after creation"""
    base_cls = obj.__class__
    base_cls_name = obj.__class__.__name__
    obj.__class__ = type(base_cls_name, (cls, base_cls),{})

答案 2 :(得分:0)

不要使用继承。在许多情况下,这是一个过度使用的抽象,这是一个例子。通过使用依赖注入,您可以轻松默认为实际的数据库,但用模拟的后端替换您的后端。

class MockReferenceData(object):
  def dbName(self):
    return 'mock'

  def totalNumberOfSeats(self):
    return '10'

class ReferenceData(object):
  def dbName(self):
    return 'real DB'

  def totalNumberOfSeats(self):
    return 'Fetch from DB'

class Car(object):

    def __init__(self, datasource_class=ReferenceData):
        self._datasource = datasource_class() # instead of creation here, you could also pass in an

    def showNumberOfSeats(self):
        print(self._datasource.totalNumberOfSeats())


real_car = Car()
real_car.showNumberOfSeats()

mocked_car = Car(datasource_class=MockReferenceData)
mocked_car.showNumberOfSeats()

另外请关注PEP8以获取Python中的编码约定。您使用“错误”缩进深度为2,导致像我这样的开发人员编辑问题。方法名也一样。