Question

I have a class hierarchy in which subclasses may optionally define a method with a given name, say do_it, and want to write a function, say do_all, in the base class that ensures that each such method will be executed, in order of class hierarchy. The closest I can get to achieving this is something like:

class A(object):
    def do_it(self):
        print 'Do A'

    def do_all(self):
        # Better, from comments and answers: vars(type(self))
        for c in reversed(self.__class__.__mro__[:-1]):
            c.do_it(self)

class B(A):
    def do_it(self):
        print 'Do B'

class C(B):
    def no_do_it(self):
        pass

class D(C):
    def do_it(self):
        print 'Do D'

This almost works as intended, for example B().do_all() gives

Do A
Do B

But it results in a duplicate call to Bs do_it for all classes descended from it. For example D().do_all() gives

Do A
Do B
Do B
Do D

How can I avoid the duplicate call to a parent's do_it from classes that do not implement it? Is this even the right way to achieve what I'm trying to do?

Was it helpful?

Solution

You can check whether the function has already been seen:

def do_all(self):
    seen = set()
    for c in reversed(self.__class__.__mro__[:-1]):
        if c.do_it not in seen:
            seen.add(c.do_it)
            c.do_it(self)

Note that in Python 2 you'll need to extract the function from the unbound method, as c.do_it.__func__ (or use six.get_unbound_function).

An alternative is to examine __dict__:

def do_all(self):
    for c in reversed(self.__class__.__mro__[:-1]):
        if 'do_it' in c.__dict__:
            c.do_it(self)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top