In my original answer (which I deleted because it was simply wrong), I said that I would consider this as follows:
class RandomPerson(Person): def __init__(self): rand_person = random.choice((John, Kyle))() self.__dict__ = rand_person.__dict__
This method is an adaptation of the Python Borg idiom ; the idea was that everything that matters to the object is contained in its __dict__ .
However, this only works when overwriting objects of the same class (what you do in the Borg idiom); the __dict__ object contains only state information related to the instance of the object, not the class of the object.
You can disable the object class as follows:
class RandomPerson(Person): def __init__(self): rand_person = random.choice((John, Kyle)) self.__class__ = rand_person
However, doing this means that calling RandomPerson will not return an instance of RandomPerson for your requirement, but Kyle or John . So this is not an option.
Here's a way to get a RandomPerson object that acts like Kyle or John , but not:
class RandomPerson(Person): def __new__(cls): new = super().__new__(cls) new.__dict__.update(random.choice((Kyle,John)).__dict__) return new
This one is very similar to the Borg idiom, except that it does it with classes instead of instance objects, and we only copy the current version of the selected dict class - it's really pretty evil: we lobotomized the RandomPerson class and (accidentally) stuck the brains of the Kyle class or John And, unfortunately, there is no indication that this has happened:
>>> rperson = RandomPerson() >>> assert isinstance(rperson,Kyle) or isinstance(rperson,John) AssertionError
Thus, we still do not have a subclass of Kyle or John . In addition, it is really really evil. Therefore, please do not do this unless you have a really good reason.
Now, assuming that you really have a good reason, the solution above should be good enough if all you need to do is make sure that you can use any class state information (class methods and attributes) from Kyle or John with using RandomPerson . However, as shown above, RandomPerson is still not a true subclass.
Close, since I can say that there is no way to actually randomly subclass an object's class when creating an AND instance to maintain the state of the class in multiple instance instances. You have to fake it.
One way to fake this is to let RandomPerson be considered a subclass of John and Kyle using an abstract base class and __subclasshook__ and adding this to your Person class. This seems to be a good solution, as the Person class is an interface and will not be used directly anyway.
Here's how to do it:
class Person(object): __metaclass__ = abc.ABCMeta def drive(self, f, t): raise NotImplementedError @classmethod def __subclasshook__(cls, C): if C.identity is cls: return True return NotImplemented class John(Person): def drive(self, f, t): print "John drove from %s to %s" % (f,t) class Kyle(Person): def drive(self, f, t): print "Kyle drove from %s to %s" % (f,t) class RandomPerson(Person): identity = None def __new__(cls): cls.identity = random.choice((John,Kyle)) new = super().__new__(cls) new.__dict__.update(cls.identity.__dict__) return new >>> type(RandomPerson()) class RandomPerson >>> rperson = RandomPerson() >>> isinstance(rperson,John) or isinstance(rperson,Kyle) True
Now RandomPerson - although it is not technically a subclass - is considered a subclass of Kyle or John , and also shares the state of Kyle or John . In fact, it will switch between them arbitrarily, every time a new instance is created (or when RandomPerson.identity changes). Another effect of this: if you have multiple instances of RandomPerson , they all share the state of any RandomPerson at the moment , i.e. rperson1 can start Kyle , and then when rperson2 is created, both rperson2 AND rperson1 can be John (or both of them can be Kyle , and then switch to John when rperson3 is created)),
Needless to say, this is a rather strange behavior. In fact, it is so strange, my suspicion is that your design needs a major overhaul. I really don't think there is a very good reason EVER to do this (except maybe a bad joke on someone).
If you do not want to mix this behavior in your Person class, you can also do this separately:
class Person(object): def drive(self, f, t): raise NotImplementedError class RandomPersonABC(): __metaclass__ = abc.ABCMeta @classmethod def __subclasshook__(cls, C): if C.identity is cls: return True return NotImplemented class John(Person, RandomPersonABC): def drive(self, f, t): print "John drove from %s to %s" % (f,t) class Kyle(Person, RandomPersonABC): def drive(self, f, t): print "Kyle drove from %s to %s" % (f,t) class RandomPerson(Person): identity = None def __new__(cls): cls.identity = random.choice((John,Kyle)) new = super().__new__(cls) new.__dict__.update(cls.identity.__dict__) return new