Object Oriented Programming (OOP)

OOP allows you to encapsulate specific behaviour into so called classes. Instances of those classes can be used to write intuitive program code. Below is an example using classes defined in rock.py to create instances of fruits and rocks and to determine their cumulative weight on a scale. Classes can represent real world objects as in this example but also abstract things like a button in a graphical user interface or a list with special sorting algorithms.

Normally you would import the classes from the module rock like this:

#from rock import Rock, Sediment, MagneticMatter, MagneticSediment, Scale, Fruit

Alternatively you can use the magic %load command to get the whole file into this ipython notebook:

# %load rock.py
# object oriented programming with Python
# Michael Wack 2015

class Fruit:
    def __init__(self, weight):
        self.weight = weight

    def __repr__(self):
        return( "fruit with weight {:.2e}".format( self.weight))


class Rock:
    # class variable
    rockcounter=0


    @classmethod
    def BlueRock(cls, weight, volume):
        return cls( color="blue", weight=weight, volume=volume)


    @staticmethod
    def density( weight, volume):
        return weight / volume

    def __init__(self, color, weight, volume=11e-6):
        self.color = color
        self.weight = weight  # SI: kg
        self.volume = volume  # SI: m^3

        Rock.rockcounter += 1
        self.no = Rock.rockcounter

        print("{}. rock created".format(Rock.rockcounter))

    def calculate_density(self):
        return Rock.density( self.weight, self.volume)  # SI: kg / m^3


    def __repr__(self):
        return( "{} rock (No {}) with a density of {:.2f}".format( self.color, self.no, self.calculate_density()))


class Sediment( Rock):
    def __init__(self, color, weight, volume=11e-6, grainsize=0):
        super().__init__(color, weight, volume)

        self.grainsize = grainsize


    def double_grainsize(self):
        self.grainsize *= 2

    def __repr__(self):
        return( "{} sediment (No {}) with a density of {:.2e} and a grainsize of {:.2e}".format( self.color, self.no, self.calculate_density(), self.grainsize))



class MagneticMatter(): # all volume normalized
    def __init__(self, magnetization=0, susceptibiltiy=0):
        self.magnetization = magnetization # Am^2 / m^3 = A/m
        self.susceptibility = susceptibiltiy # volume normailzed -> no units

    def induced_magnetization(self, external_field_H): # SI A/m
        return self.susceptibility * external_field_H

    def total_magnetization(self, external_field_H): # SI A/m
        return self.induced_magnetization(external_field_H) + self.magnetization



class MagneticSediment( MagneticMatter, Sediment):
    def __init__(self, color, weight, volume, grainsize=0, magnetization=0, susceptibility=0):
        MagneticMatter.__init__( self, magnetization, susceptibility)
        Sediment.__init__( self, color, weight, volume)

    def magnetic_moment(self):
        return self.magnetization * self.volume


# class to determine weight of other object instances
class Scale():
    def __init__(self, weight_limit=100):
        self.weight_limit = weight_limit
        self.instances = []

    def put_on(self, inst):
        if inst in self.instances:
            print("{} is already on scale.".format(inst))
        else:
            self.instances.append(inst)
            print("{} placed on scale.".format(inst))

    def take_off(self, inst):
        try:
            self.instances.remove(inst)
            print("{} removed.".format(inst))
        except KeyError:
            print("{} was not on scale.".format(inst))

    @property
    def weight(self):
        weight = 0
        for i in self.instances:
            weight += i.weight
        if weight > self.weight_limit:
            print("Scale overloaded.")

        return weight
apple = Fruit(weight=0.2)
apple
fruit with weight 2.00e-01
my_first_rock = Rock(color='red', weight=10)
1. rock created
my_first_rock
red rock (No 1) with a density of 909090.91
my_second_rock = Sediment(color='yellow', weight=12)
2. rock created
blue_rock = Rock.BlueRock(weight=3, volume = 0.1)
3. rock created
blue_rock
blue rock (No 3) with a density of 30.00
my_last_rock = MagneticSediment(color='purple', weight=1, volume=0.01, magnetization=1e-3, susceptibility=5e-3)
4. rock created
my_last_rock.induced_magnetization(external_field_H = 40)
0.2
my_last_rock.total_magnetization(external_field_H = 40)
0.201
myscale = Scale(weight_limit = 20)
myscale
<__main__.Scale at 0x7f45b01a36d8>
myscale.put_on( blue_rock)
blue rock (No 3) with a density of 30.00 placed on scale.
myscale.weight
3
myscale.put_on( my_second_rock)
yellow sediment (No 2) with a density of 1.09e+06 and a grainsize of 0.00e+00 placed on scale.
myscale.weight
15
myscale.put_on( my_second_rock)
yellow sediment (No 2) with a density of 1.09e+06 and a grainsize of 0.00e+00 is already on scale.
myscale.weight
15
myscale.put_on( my_first_rock)
red rock (No 1) with a density of 909090.91 placed on scale.
myscale.weight
Scale overloaded.
25
myscale.take_off( blue_rock)
blue rock (No 3) with a density of 30.00 removed.
myscale.weight
Scale overloaded.
22