cisprague
cisprague

Reputation: 81

How to define base class arguments based upon child class type in Python?

Parent Class: class Body(object)

I have a parent class characterising the classical mechanics definition of a physical body. Such as it is, this parent class has the attributes: name, mass, position, velocity.

from Utilities import *

class Body(object):

  '''
  In classical mechanics a physical body is collection of
  matter having properties including mass, velocity, momentum
  and energy. The matter exists in a volume of three-dimensional
  space called its extension.
  '''

  def __init__(self, name, mass):
    if isinstance(name, str) and isinstance(mass, float):
      #Name
      self.name = name
      #Mass
      self.mass = mass
      #Position record
      self.positions = np.empty(shape = (0, 3), dtype = float)
      #Velocity record
      self.velocities = np.empty(shape = (0, 3), dtype = float)
      pass
    else:
      raise TypeError('Name and mass must be string and float, respectivly.')
    return None

Child Class: class Planet(Body)

Additionally, I have a child class characterising Planets, which are fundamentally physical bodies, and thus should inherit the abstract physical attributes of such, namely: name, mass, position, velocity.

class Planet(Body):
  def __init__(self, name, mass = float):
    self.mass = Planet_Mass(name)
    Body.__init__(self, name, self.mass)

Usage

#Import the necesary modules
from context import Planet
#Instantiate Earth as a massive celestial object
Earth = Planet('Earth')
#Print the mass of the planet [10^24 kg]
print 'The mass of Earth is: ', Earth.mass, ' [10^24 kg]'

Result

The mass of Earth is: 5.97 [10^24 kg]

Problem

Fundamentally, all physical bodies have mass. However, in the context of this simulation, the method by which mass is determined differs between the various child classes of class Body(object), namely: class Planet(Body), class Satellite(Body), etc.

Is there anyway to alter the initialisation of my child classes to be less redundant?

Essentially, I would like to remove the necessity of stating self.mass = Planet_Mass(name) in class Planet(Body).

Upvotes: 2

Views: 2423

Answers (1)

kindall
kindall

Reputation: 184101

Don't accept mass in your Planet class constructor since you're not going to use it.

Don't set self.mass in your Planet class constructor since the parent constructor does it.

class Planet(Body):
    def __init__(self, name):
        Body.__init__(self, name, Planet_Mass(name))

If you do want to accept mass in your Planet class and allow it to override the mass from the lookup table (or maybe some planets aren't in the lookup table), do it like this:

class Planet(Body):
    def __init__(self, name, mass=None):
        if mass is None:
            Body.__init__(self, name, Planet_Mass(name))
        else:
            Body.__init__(self, name, mass)

earth   = Planet("Earth")
trantor = Planet("Trantor", 1.792e25)   # Asimov's Trantor; ~3x Earth mass

If you want to go further and avoid the if statement in your subclasses' __init__ then you can put the desired behavior on the Body class. The basic idea is that you define a method to calculate the mass from the name. On the base class, this method would do nothing. On your subclasses, it gets the mass from the lookup table. Then you don't even need __init__ in your subclasses.

class Body(object):

     def __init__(self, name, mass=None):
         if mass is None:
             mass = self._name2mass(name)
         # followed by the argument checking and attribute setting
         # especially, you must check that mass is not None

     @staticmethod
     def _name2mass(name): return None

class Planet(Body):

     @staticmethod
     def _name2mass(name): return Planet_Mass(name)
     # or just implement Planet_Mass here!

I've used @staticmethod because looking up a body's mass by its name does not require access to the instance (or the class, for that matter).

Upvotes: 3

Related Questions