12 KiB
The smell of classes¶
The "data bundle" smell¶
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
# 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)) # ??
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).
masses = [10.0, 12.0]
velocities = [0.9, 0.1]
print(momentum(masses[0], velocities[0]))
print(momentum(masses[1], velocities[1]))
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']))
All of the functions above can be rewritten as a function of this particle "instance", eliminating the bookkeeping for the individual parameters.
def momentum(particle):
return particle['mass'] * particle['velocity']
print(momentum(particle1))
print(momentum(particle2))
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"
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))
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¶
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)
particle1.velocity
particle2.mass
particle1.__dict__
Class methods¶
def momentum(particle):
return particle.mass * particle.velocity
print(momentum(particle1))
print(momentum(particle2))
class Particle:
def __init__(self, mass, velocity):
self.mass = mass
self.velocity = velocity
def momentum(self):
return self.mass * self.velocity
def update_position(self, position, dt):
return position + self.velocity * dt
particle1 = Particle(10.0, 0.9)
particle1.update_position(1, 5)
We have been using class instances and methods all along...
s = 'A scanner Darkly'
s.capitalize()
x = set(['apple', 'banana', 'apple', 'pineapple'])
x
x.union(['banana', 'kiwi'])