import inspect

class A:
    def __init__(self, bar):
        self.bar = bar
    def foo(self):
        return self.__class__.__make_me__(A, self.bar)
    @classmethod
    def __make_me__(cls, base, *args, **kwargs):
        if base is A:
            return A(*args, **kwargs)
        else:
            return super(A, cls).__make_me__(base, *args, **kwargs)

class B(A):
    def __init__(self, bar, baz):
        self.bar = bar
        self.baz = baz
    @classmethod
    def __make_me__(cls, base, *args, **kwargs):
        if base is B:
            return B(*args, **kwargs)
        elif base is A:
            bound = inspect.signature(A).bind(*args, **kwargs)
            return cls.__make_me__(B, bound.arguments['bar'], None)
        else:
            super(A, cls).__make_me__(base, *args, **kwargs)

class C(A):
    def __init__(self):
        pass
    @classmethod
    def __make_me__(cls, base, *args, **kwargs):
        if base is C:
            return C(*args, **kwargs)
        elif base is A:
            bound = inspect.signature(A).bind(*args, **kwargs)
            return cls.__make_me__(C)
        else:
            return super(C, cls).__make_me__(base, *args, **kwargs)

class D(C, B):
    def __init__(self, bar, baz, spam):
        self.bar = bar
        self.baz = baz
        self.spam = spam
    @classmethod
    def __make_me__(cls, base, *args, **kwargs):
        if base is D:
            return D(*args, **kwargs)
        elif base is B:
            bound = inspect.signature(B).bind(*args, **kwargs)
            return cls.__make_me__(D, bound.arguments['bar'],
                                   bound.arguments['baz'], 'hello')
        else:
            return super(D, cls).__make_me__(base, *args, **kwargs)

if __name__ == '__main__':
    assert D('a', 'b', 'c').foo().__class__ is D
