Абстрактен клас + mixin + множествено наследяване в python

И така, мисля, че кодът вероятно обяснява какво се опитвам да направя по-добре, отколкото мога с думи, така че ето го:

import abc

class foo(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def bar(self):
        pass


class bar_for_foo_mixin(object):
    def bar(self):
        print "This should satisfy the abstract method requirement"


class myfoo(foo, bar_for_foo_mixin):
    def __init__(self):
        print "myfoo __init__ called"
        self.bar()

obj = myfoo()

Резултатът:

TypeError: Can't instantiate abstract class myfoo with abstract methods bar

Опитвам се да накарам класа mixin да удовлетвори изискванията на класа abstract/interface. какво ми липсва


person mluebke    schedule 05.05.2009    source източник


Отговори (2)


Не трябва ли наследяването да е обратното? В MRO foo в момента идва преди bar_for_foo_mixin и след това с право се оплаква. С class myfoo(bar_for_foo_mixin, foo) трябва да работи.

И не съм сигурен дали дизайнът на вашия клас е правилният начин да го направите. Тъй като използвате mixin за внедряване на bar, може би е по-добре да не извличате от foo и просто да го регистрирате с класа 'foo' (т.е. foo.register(myfoo)). Но това е само усещането ми.

За пълнота ето документацията за ABC.

person nikow    schedule 05.05.2009
comment
Добро решение, промяната на реда на наследяване върши работа. P.S. кодът беше опростен, за да илюстрира точка. Моят сценарий в реалния свят е много по-сложен с много потенциални миксини и много myfoos. - person mluebke; 06.05.2009

мисля (тествано в подобен случай), че обръщането на базовите класове работи:

class myfoo(bar_for_foo_mixin, foo):
    def __init__(self):
        print "myfoo __init__ called"
        self.bar()

така че в mro() ще намери конкретна версия на bar(), преди да намери абстрактната. Нямам представа дали това всъщност се случва на заден план.

Наздраве, Ларс

PS: кодът, който работеше в python 2.7 (python 3 има различен начин за задаване на метакласове) беше:

class A(object):
    __metaclass__ = abc.ABCMeta

    @abc.abstractmethod
    def do(self):
        pass

class B(object):
    def do(self):
        print "do"

class C(B, A):
    pass

c = C()
person Lars    schedule 06.09.2013
comment
Благодаря за този отговор. В py27 вашият код, който работи, наистина налага присъствието на do() в клас C. Въпреки това, в py3 (тествах само в py35), това вече не е така. В клас B можете да замените тялото с pass и екземпляр c щастливо ще бъде създаден. Знаете ли защо може да е така? - person Caleb Hattingh; 17.03.2016
comment
@cjrh: тялото на B ли имаш предвид или тялото на B.do? Ако е първият, би било наистина странно, защото някак ще се противопостави на целта на abc.. - person Lars; 18.02.2017
comment
Просто опитайте кода си под PS: кодът, който работи, беше, но променете B на class B(object): pass. Ще видите, че екземплярът c е създаден успешно. Опитах на Python 3.6, същият резултат. - person Caleb Hattingh; 19.02.2017
comment
Добре, виждам какво става. __metaclass__ вече не е начинът, по който декларирате метаклас в Python 3. Вместо това трябва да посочите метакласа в аргументите на класа, например class A(metaclass=abc.ABCMeta): <etc>. След това работи като за Python 2. - person Caleb Hattingh; 19.02.2017
comment
проверка, добавено към отговор - person Lars; 14.03.2017