Reputation: 21
I have a base class with two subclasses, say Car with subclasses HeavyCar and LightCar. Is it possible to have the creation of a base class return an object of a subclass dependent of a variable? Like this:
weightA = 2000
myAcar = Car(weigthA) # myAcar is now of type HeavyCar
weightB = 500
myBcar = Car(weightB) # myBcar is now of type LightCar
I know that the normal way to do this would be to look at the weight variable and then decide what type of object I want and then create that specific object. However I would like to leave it up to my class to decide which type it should be and not have to bother about that outside the class, i.e not have to look at the variable weight at all.
Upvotes: 2
Views: 950
Reputation: 522075
Even if it's somehow possible to do this, it's not really a very sane object design. Things can be too dynamic at some point. The sane solution would simply be a factory function:
class LightCar(Car):
maxWeight = 500
def __init__(self, weight):
assert(weight <= self.maxWeight)
self._weight = weight
# analogous for HeavyCar
def new_car(weight):
if weight <= LightCar.maxWeight:
return LightCar(weight)
..
From the point of view of the consumer, it makes little difference:
import cars
car = cars.new_car(450)
print(type(car))
Whether you write cars.new_car(450)
or cars.Car(450)
hardly makes any difference, except that the former is a dedicated factory function which does exactly what you're wanting: it returns a LightCar
or HeavyCar
depending on the weight.
Upvotes: 3
Reputation: 40693
You can override __new__
to make it return the desired type. However, it would just be simpler to define a function that does the same, as it would be less prone to errors.
using __new__
class Car(object):
def __init__(self, weight):
self.weight = weight
def __new__(cls, weight):
if cls is Car:
if weight > 1000:
return object.__new__(HeavyCar)
else:
return object.__new__(LightCar)
else:
return object.__new__(cls)
class LightCar(Car):
def __init__(self, weight):
super(LightCar, self).__init__(weight)
self.speed = "fast"
class HeavyCar(Car):
pass
assert isinstance(Car(10), LightCar)
assert Car(10).weight == 10 # Car.__init__ invoked
assert hasattr(Car(10), "speed") # LightCar.__init__ invoked as well
assert isinstance(Car(1001), HeavyCar)
assert Car(1001).weight == 1001 # Car.__init__ invoked
Using a function
def create_car(weight):
if weight > 1000:
return HeavyCar(weight)
else:
return LightCar(weight)
assert isinstance(create_car(10), LightCar)
Upvotes: 4
Reputation: 6320
It may be possible by overwriting the __new__ method, but that would be complex and take a long time.
... Just use a function or have a child class that uses multiple inheritance to contain light car and heavy car. If you make another child class you will have to deal with method resolution conflicts.
def car(weight):
if weight > 2:
return HeavyCar(weight)
return LightCar(weight)
__new__
http://rafekettler.com/magicmethods.html
https://www.python.org/download/releases/2.2/descrintro/#new
mro
http://www.phyast.pitt.edu/~micheles/python/meta2.html
Method Resolution Order (MRO) in new style Python classes
Upvotes: 0