2024-heraklion-scientific-p.../notebooks/01a_Classes.ipynb
2024-08-27 15:52:41 +03:00

11 KiB

The smell of classes

The "data bundle" smell

In [1]:
def momentum(mass, velocity):
    return mass * velocity

def energy(mass, velocity):
    return 0.5 * mass * velocity ** 2

def update_position(velocity, position, dt):
    return position + velocity * dt
In [2]:
# Naive
mass1 = 10.0
velocity1 = 0.9

mass2 = 12.0
velocity2 = 0.1

print(momentum(mass1, velocity1))
print(momentum(mass2, velocity2))
print(momentum(mass1, velocity2))  # ??
9.0
1.2000000000000002
1.0

We have two parameters that will be sent to these functions over and over again: mass and velocity.

Moreover, the parameters cannot be mixed up (e.g. the velocity of one particle with the mass of another).

In [3]:
masses = [10.0, 12.0]
velocities = [0.9, 0.1]

print(momentum(masses[0], velocities[0]))
print(momentum(masses[1], velocities[1]))
9.0
1.2000000000000002
In [4]:
particle1 = {'mass': 10.0, 'velocity': 0.9}
particle2 = {'mass': 12.0, 'velocity': 0.1}

print(momentum(particle1['mass'], particle1['velocity']))
print(momentum(particle2['mass'], particle2['velocity']))
9.0
1.2000000000000002

All of the functions above can be rewritten as a function of this particle "instance", eliminating the bookkeeping for the individual parameters.

In [5]:
def momentum(particle):
    return particle['mass'] * particle['velocity']

print(momentum(particle1))
print(momentum(particle2))
9.0
1.2000000000000002

An annoying thing of this solution is that we have to remember the name of the keys in the dictionary, and the solution is sensitive to typos.

To solve this, we could write a function to build a particle, a.k.a a "constructor"

In [6]:
def init_particle(mass, velocity):
    self = {
        'mass': mass,
        'velocity': velocity
    }
    return self

particle1 = init_particle(10.0, 0.9)
particle2 = init_particle(12.0, 0.1)
print(momentum(particle1))
print(momentum(particle2))
9.0
1.2000000000000002

particle1 and particle2 are called "instances" of the particle "class".

Python classes are a way to formalize this pattern: creating a bundle of data that belongs together. E.g. the parameters of an experiment, the results of a simulation, etc.

Introducing classes as a data bundle template

In [7]:
class Particle:
    def __init__(self, mass, velocity):
        self.mass = mass
        self.velocity = velocity

particle1 = Particle(10.0, 0.9)
particle2 = Particle(12.0, 0.1)
In [8]:
particle1.velocity
Out[8]:
0.9
In [9]:
particle2.mass
Out[9]:
12.0
In [10]:
particle1.__dict__
Out[10]:
{'mass': 10.0, 'velocity': 0.9}

Class methods

In [11]:
def momentum(particle):
    return particle.mass * particle.velocity

print(momentum(particle1))
print(momentum(particle2))
9.0
1.2000000000000002
In [12]:
class Particle:
    def __init__(self, mass, velocity):
        self.mass = mass
        self.velocity = velocity

    def momentum(self):
        return self.mass * self.velocity

particle1 = Particle(10.0, 0.9)
print(particle1.momentum())
9.0

We have been using class instances and methods all along...

In [13]:
s = 'A scanner Darkly'
s.capitalize()
Out[13]:
'A scanner darkly'
In [14]:
x = set(['apple', 'banana', 'apple', 'pineapple'])
x
Out[14]:
{'apple', 'banana', 'pineapple'}
In [15]:
x.union(['banana', 'kiwi'])
Out[15]:
{'apple', 'banana', 'kiwi', 'pineapple'}
In [ ]: