There are many different ways to program in Python. In this class we will be (mostly) programing in what is called the Procedural style. However, at its heart, Python is an Objected-Oriented programming language. The Objected-Oriented style of programming has many advantages, but is less straightforward than Procedural programming
As you progress in your programming life, there will come a time when you find that moving to the Objected-Oriented paradigm will make your life easier.
I would like to say I created this introduction to aid in your exploration of Objected-Oriented Python, but I did not. I just shamelessly stole and modified Brett Morris' awesome work.
In our introduction to Python, we used a dataset of main belt asteroids.
This dataset contained the following data for the asteroids:
Let us work with this data from an Objected-Oriented point of view.
To create a new object, you use the class
command, rather than the def
command that you would use for functions,
class SpaceRock(object):
We've named the new object SpaceRock
- object names in python should be uppercase without underscores separating words (whereas functions are usually all lowercase and words are separated by underscores).
__init__
methodNow we will define how you call the SpaceRock
constructor (the call that creates new SpaceRock
objects). Let's say you want to be able to create a asteroid like this...
new_asteroid = SpaceRock(name=my_name, ab_mag=my_ab_mag, albedo=my_albedo)
All Python objects get initialized with a function called __init__
defined within the class, like this:
class SpaceRock(object):
def __init__(self, name=None, ab_mag=None, albedo=None):
You define the __init__
function like all other functions, except that the first argument is always called self
. This self
is the shorthand variable that you use to refer to the SpaceRock
object within the __init__
method.
Objects have attributes, which are like variables stored on an object. We'll want to store the values above into the SpaceRock
object, each with their own attribute, like this:
class SpaceRock(object):
def __init__(self, name=None, ab_mag=None, albedo=None):
self.name = name
self.ab_mag = ab_mag
self.albedo = albedo
Each attribute is defined by setting self.<attribute name> = <value>
. All attributes should be defined within the __init__
method.
In [ ]:
import numpy as np
from astropy import units as u
In [ ]:
class SpaceRock(object):
def __init__(self, name=None, ab_mag=None, albedo=None):
self.name = name
self.ab_mag = ab_mag
self.albedo = albedo
In [ ]:
# Create some fake data:
my_name = "Geralt of Rivia"
my_ab_mag = 5.13
my_albedo = 0.131
# Initialize a SpaceRock object:
new_asteroid = SpaceRock(name=my_name, ab_mag=my_ab_mag, albedo=my_albedo)
We can see what values are stored in each attribute like this:
In [ ]:
new_asteroid.name
In [ ]:
new_asteroid.albedo
So far this just looks like another way to store your data. It becomes more powerful when you write methods for your object.
Methods can be thought of as functions associated with an object.
You can now access the attributes of the object within methods by calling self.<attribute name>
.
Let's make a simple method for the SpaceRock
object, which determines the size of the asteroid.
In [ ]:
class SpaceRock(object):
def __init__(self, name=None, ab_mag=None, albedo=None):
self.name = name
self.ab_mag = ab_mag
self.albedo = albedo
def diameter(self):
result = (1329.0 / np.sqrt(self.albedo)) * (10 ** (-0.2 * self.ab_mag))
return result * u.km
In [ ]:
new_asteroid = SpaceRock(name=my_name, ab_mag=my_ab_mag, albedo=my_albedo)
new_asteroid.diameter()
In [ ]:
from astropy.table import QTable
In [ ]:
rock_table = QTable.read('MainBelt_small.csv', format='ascii.csv')
In [ ]:
print(rock_table)
In [ ]:
my_name = rock_table['Name']
my_ab_mag = rock_table['H']
my_albedo = rock_table['A']
rocks = SpaceRock(name=my_name, ab_mag=my_ab_mag, albedo=my_albedo)
In [ ]:
rocks.diameter()
One of the nice things about creating a Class
is that all of the methods within the Class
know about each other.
For example: I want to create a new method that uses the results of the diameter
method I already defined.
Easy, just use the variable self.diameter()
in my new method
In [ ]:
class SpaceRock(object):
def __init__(self, name=None, ab_mag=None, albedo=None):
self.name = name
self.ab_mag = ab_mag
self.albedo = albedo
def diameter(self):
result = (1329.0 / np.sqrt(self.albedo)) * (10 ** (-0.2 * self.ab_mag))
return result * u.km
def two_diameter(self):
result = 2.0 * self.diameter()
return result
In [ ]:
rocks = SpaceRock(name=my_name, ab_mag=my_ab_mag, albedo=my_albedo)
In [ ]:
rocks.diameter()
In [ ]:
rocks.two_diameter()
As you modify your Class
all of the methods within the Class
know about the modifications.
For example: Let us add some more attributes to our Asteroid data.
In [ ]:
class SpaceRock(object):
def __init__(self, name = None, ab_mag = None, albedo = None,
semi_major= None, ecc = None):
self.name = name
self.ab_mag = ab_mag
self.albedo = albedo
self.semi_major = semi_major
self.ecc = ecc
def diameter(self):
result = (1329.0 / np.sqrt(self.albedo)) * (10 ** (-0.2 * self.ab_mag))
return result * u.km
def two_diameter(self):
result = 2.0 * self.diameter()
return result
def find_perihelion(self):
result = self.semi_major * ( 1.0 - self.ecc )
return result * u.AU
In [ ]:
my_name = rock_table['Name']
my_ab_mag = rock_table['H']
my_albedo = rock_table['A']
my_semi_major = rock_table['a']
my_ecc = rock_table['ecc']
more_rocks = (SpaceRock(name=my_name, ecc = my_ecc, semi_major=my_semi_major,
ab_mag=my_ab_mag, albedo=my_albedo))
In [ ]:
more_rocks.diameter()
In [ ]:
more_rocks.find_perihelion()
In [ ]:
for idx,value in enumerate(more_rocks.find_perihelion()):
rock_name = more_rocks.name[idx]
my_string = "The Asteroid {0} has a perihelion distance of {1:.2f}".format(rock_name, value)
print(my_string)
In [ ]:
class SpaceRock(object):
"""Container for Asteroids"""
def __init__(self, name = None, ab_mag = None, albedo = None,
semi_major= None, ecc = None):
"""
Parameters
----------
name : string
Name of the target
ab_mag : array-like
Absolute Magnitude of each Asteroid
albedo : array-like
Albedo of each Asteroid
semi_major : array-like
Semi Major Axis of each Asteroid in AU
ecc : array-like
Eccentricity of each Asteroid
"""
self.name = name
self.ab_mag = ab_mag
self.albedo = albedo
self.semi_major = semi_major
self.ecc = ecc
def diameter(self):
"""
Determine the diameter (in km) of the Asteroids
"""
result = (1329.0 / np.sqrt(self.albedo)) * (10 ** (-0.2 * self.ab_mag))
return result * u.km
def two_diameter(self):
"""
Determine twice the diameter (in km) of the Asteroids
"""
result = 2.0 * self.diameter()
return result
def find_perihelion(self):
"""
Determine the perihelion distance of the Asteroids in AU
"""
result = self.semi_major * ( 1.0 - self.ecc )
return result * u.AU
In [ ]:
rocks = (SpaceRock(name=my_name, ecc = my_ecc, semi_major=my_semi_major,
ab_mag=my_ab_mag, albedo=my_albedo))
Now you can see the documentation on the module within the Notebooks by typing
rocks?
...you can see the documentation for each method by typing
rocks.diameter?
In [ ]:
rocks?
In [ ]:
rocks.diameter?