(original) (raw)

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