无法在所有基类上调用相同的方法

时间:2017-02-16 13:25:58

标签: python

我想在我班级的所有mixins上调用相同的方法。以下是两种变体:

class MixinA(object):
    def get_id(self):
        return "A"


class MixinB(object):
    def get_id(self):
        return "B"


class Base(object):
    def get_id(self):
        for base_class in inspect.getmro(self.__class__):
            return ",".join(base_class.get_id())


class Instance(MixinA, MixinB, Base):
    pass


class MyTestCase(unittest.TestCase):
    def test_multiple_mixin_methods(self):
        """
        Sadly, we cannot call all mixin methods.
        :return:
        """

        ids = set(Instance().get_id())
        print(ids)
        assert ids == {"A", "B"}

可悲的是,这失败了。我只得到了A'背部。我想要一个包含' A'和' B',顺序并不重要。

我在这里做错了什么?

非常感谢

2 个答案:

答案 0 :(得分:1)

你需要的是打电话给超级。 super调用MRO中的下一个方法,因此您将调用A => B. 你不需要Base类。

class MixinA(object):
    def get_id(self):
        return "A", super(MixinA, self).get_id()


class MixinB(object):
    def get_id(self):
        return "B"


class Instance(MixinA, MixinB):
    pass

首先,Instance().get_id()会调用A.get_id() 对super的调用将调用MRO中的下一个方法B

<强>更新

class MixinA(object):
    def get_id(self):
        return "A"


class MixinB(object):
    def get_id(self):
        return "B"


class Base(object):
    def get_id(self):
       return set([base_class.get_id(self) for base_class in inspect.getmro(self.__class__)[2:-1]])


class Instance(Base, MixinA, MixinB):
    pass

不像mixin独立之前那样。在此示例中,您需要Base类在MRO中的第一个

答案 1 :(得分:1)

这个片段通过了你的测试,但它很脆弱,恕我直言,这是一个确定的设计气味。此外,它要求Base在mro中排在第一位(但在使用基类和mixins时,这通常是你想要的):

class MixinA(object):
    def get_id(self):
        return "A"


class MixinB(object):
    def get_id(self):
        return "B"


class Base(object):
    def get_id(self):
        ids = set()
        mro = self.__class__.__mro__
        start = mro.index(Base) + 1
        for cls in mro__[start:-1]:
            get_id = getattr(cls, "get_id", None)
            if get_id:
                id = cls.get_id(self)
                ids.add(id)
        return ids

class Instance(Base, MixinA, MixinB):
    pass

你可以通过super来调用它(好吧,有点......),但它也不是很漂亮,当你添加第三个mixin时,事情开始变得难看(测试在MixinC基础中使用和不使用Instance的代码段来查看我的意思):

def get_super_id(id, cls, obj):
    try:
        supid = super(cls, obj).get_id()
        return id, supid 
    except AttributeError:
        return id

class MixinA(object):
    def get_id(self):
        return get_super_id("A", MixinA, self)


class MixinB(object):
    def get_id(self):
        return get_super_id("B", MixinB, self)

class MixinC(object):
    def get_id(self):
        return get_super_id("C", MixinC, self)


class Base(object):
    pass

class Instance(Base, MixinA, MixinB, MixinC):
    pass

实际上,我对整个想法最不喜欢的是get_id返回单个值或集合。这以不可预测的方式打破了期望。