Question

I want to define a class AorB, such that all A's are AorB's, and all B's are AorB's, and these are all the AorB's. Of course, A and B should be subclasses of AorB. The problem is in AorB.__init__, when I can't convince self it should be something else. I can define an AorB factory, but I'd rather have AorB constructor if possible.

class AorB:
    def __init__(self,par):
        if par: self=A(par) #!
        else: self=B()      #!
    @staticmethod
    def from_par(par):
        if par: return A(par)
        else: return B()
class A(AorB):
    def __init__(self,par):
        self.par=par
class B(AorB):
    def __init__(self):
        pass
print(
    AorB.from_par(5),
    AorB.from_par(0),
    AorB(5),
    AorB(0),
    sep="\n")

I know assignment to self doesn't work here, but I just wanted to show intention. As I said, factory (from_par) works fine, I just want to call it as AorB, not as AorB.from_par.

PS. I know, __init__ is probably too late, type of self is already determined. Feel free to use metaclasses in your answer. It's time I learn something useful about them. :-)

Was it helpful?

Solution

You can't, not with __init__. Once __init__ is being called, the instance is already created.

You want a factory function instead:

class AorB: pass

class A(AorB):
    def __init__(self,par):
        self.par=par

class B(AorB):
    def __init__(self):
        pass

def AorB(par):
    return A(par) if par else B()

From an API point of view, there is no difference; AorB is a callable that produces either an A() or a B() instance.

The other possible route involves defining a __new__ function instead; this is the class constructor:

class AorB:
    def __new__(cls, par=None):
        if cls is not AorB: return super().__new__(cls)
        return super().__new__(A) if par else super().__new__(B)

class A(AorB):
    def __init__(self, par):
        self.par = par

class B(AorB):
    def __init__(self, par=None):
        pass

Which is just a more involved factory function, really. Note that the __new__ method returns the result of super().__new__ of one of the subclasses based on par, so both A and B will be passed a par parameter, wether they want one or not.

The if cls is not AorB line is needed to allow instantiating A() or B() directly; you can omit that line if that is not a requirement and only the AorB factory class is used.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top