ampron
ampron

Reputation: 3666

Python Classes: Variable subclass creation in the base class's methods

Here's the coding problem I am trying to solve... I have a base class, let's say Animal, and it has two subclasses, say Dog and Cat. My class Animal has a method, make_baby(), that both Dog and Cat will inherit. The trick I'm having trouble pulling off is that I want the return value to be a new instance of the subclass that called the function but with different attribute values, i.e. Dog.make_baby() should return a new Dog and Cat.make_baby() will return a new Cat.

I previously tried returning "type(self)()", but this is no good because type() return a type object, not a class.

Here is the full example code:

Class Animal():
  def __init__(self, color):
    self.color = color
  def make_baby():
    new_color = rand_color # a randomly chosen color
    return #??? new class of the same type that called the method

Class Dog(Animal):
  def pet():
    print '*pant*'

Class Cat(Animal):
  def pet():
    print 'purrr'

So I'd like to avoid writing a make_baby() method for Dogs and Cats because the idea is that the method is exactly the same except for the returned class. I'd also like to avoid a bunch of if statements because I'd like to make and arbitrarily large number of subclasses to Animal.

Upvotes: 3

Views: 2814

Answers (3)

kindall
kindall

Reputation: 184280

You wrote:

this is no good because type() return a type object, not a class.

A type is a class, if you're using new-style classes. If you're using Python 3, you're set; all Python 3 classes are "new-style." If you're using Python 2.x, derive your class from object (or from something else that derives from object, like any built-in Python type).

But what you really want here is a class method, where you get a reference to the class passed in automatically.

class Animal(object):

  def __init__(self, color):
    self.color = color

  @classmethod
  def make_baby(cls):
    return cls(rand_color)   # randomly-chosen color

You can call it on the class (e.g. Animal.make_baby() or Dog.make_baby()) or on an instance; either way the method still receives the class as the first argument.

Upvotes: 10

Matt Luongo
Matt Luongo

Reputation: 14849

You approach will totally work! Just use new style classes.

Class Animal(object):
  def __init__(self, color):
    self.color = color
  def make_baby(self):
    new_color = rand_color # a randomly chosen color
    return type(self)(new_color)

Class Dog(Animal):
  def pet():
    print '*pant*'

Class Cat(Animal):
  def pet():
    print 'purrr'

However, if make_baby(self) is not relying on details of self, what you want is a class-wide factory method, like in @Kindall's answer.

Upvotes: 3

miracle2k
miracle2k

Reputation: 32077

type() can be used to construct entirely new classes. What you want is:

class Animal():
  def __init__(self, color):
    self.color = color

  def make_baby(self):
    new_color = rand_color # a randomly chosen color
    return self.__class__(new_color)

Upvotes: 3

Related Questions