36

So, I think the code probably explains what I'm trying to do better than I can in words, so here goes:

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

The result:

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

I'm trying to get the mixin class to satisfy the requirements of the abstract/interface class. What am I missing?

2 Answers 2

32

Shouldn't the inheritance be the other way round? In the MRO foo currently comes before bar_for_foo_mixin, and then rightfully complains. With class myfoo(bar_for_foo_mixin, foo) it should work.

And I am not sure if your class design is the right way to do it. Since you use a mixin for implementing bar it might be better not to derive from foo and just register it with the 'foo' class (i.e. foo.register(myfoo)). But this is just my gut feeling.

For completeness, here is the documentation for ABCs.

Sign up to request clarification or add additional context in comments.

1 Comment

Good call, changing the order of inheritance does the trick. P.S. the code was simplified to illustrate a point. My real world scenario is much more complex with lots of potential mixins and lots of myfoos.
1

i think (tested in similar case) that reversing the baseclasses works:

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

so in the mro() it would find a concrete version of bar() before it finds the abstract one. No idea if this is actually what happens in the background though.

Cheers, Lars

PS: the code that worked in python 2.7 (python 3 has a different way to set metaclasses) was:

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

5 Comments

Thanks for this answer. In py27, your code that worked does indeed enforce the presence of do() in class C. However, in py3 (I only tested in py35), this is no longer the case. In class B you can replace the body with pass, and instance c will happily be created. Do you know why this might be?
@cjrh: do you mean the body of B or the body of B.do? If the first, it would be really strange because it would sort of defy the purpose of abc's ..
Just try your code under "PS: the code that worked was", but change B to class B(object): pass. You will see that instance c is successfully created. I tried on Python 3.6, same result.
Ok, I see what's going on. __metaclass__ is no longer how you declare a metaclass in Python 3. Instead you have to specify the metaclass in the class args, e.g., class A(metaclass=abc.ABCMeta): <etc>. Then it works as for Python 2.
check, added to answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.