Oregon State University
2025-01-13
Classes: organize data, methods, and functions
Objects: manifestations of classes
We can use object-oriented programming to cleanly organize behaviors and data in our code.
It provides a framework for classifying distinct concepts into comprehensible sizes. These smaller conceptual units facilitate cleaner, more scalable modeling.
Everything in Python is an object.
All objects in Python have attributes and methods.
Even functions are objects!
Classes define logical collections of attributes and methods describing a kind of object, and how to create objects of that kind.
Choose classes to encapsulate internal data and functions for different types of objects.
class Particle(object):
"""A particle is a constituent unit of the universe."""
# class body definition here
What else do classes include?
Data universally applicable to all objects of the class
Data with different values for each instance of the class
Example: particle position.
import particle as p
# create empty list to hold observed particle data
obs = []
# append first particle
obs.append(p.Particle())
# assign its position
obs[0].r = {'x': 100.0, 'y': 38.0, 'z': -42.0}
# append second particle and assign its position
obs.append(p.Particle())
obs[1].r = {'x': 0.01, 'y': 99.0, 'z': 32.0}
# print positions of each particle
print(obs[0].r)
print(obs[1].r)
Using instance variables, we can store all data associated with a particle observation (position, mass, charge, spin, etc.) without much more complexity
(Hopefully the value of this reduced complexity is obvious.)
How to accomplish in class definition? Constructor: __init()__
function.
Constructor: __init__()
function, executed upon instantiation of object.
Constructor not required; every class inherits default constructor from object
.
Tip: good to initialize all instance variables in constructor, to ensure they are initialized when you need them.
# particle.py
class Particle(object):
"""A particle is a constituent unit of the universe.
Attributes
----------
c : charge in units of [e]
m : mass in units of [kg]
r : position in units of [meters]
"""
roar = "I am a particle!"
def __init__(self):
"""Initializes the particle with default values for charge c, mass m, and position r.
"""
self.c = 0
self.m = 0
self.r = {'x': 0, 'y': 0, 'z': 0}
self
argument required since function is method; binds to specific instance of the class.
More efficient: specify data values upon initialization:
# particle.py
class Particle(object):
"""A particle is a constituent unit of the universe.
Attributes
----------
c : charge in units of [e]
m : mass in units of [kg]
r : position in units of [meters]
"""
roar = "I am a particle!"
def __init__(self, charge, mass, position):
"""Initializes the particle with supplied values for charge c, mass m, and position r.
"""
self.c = charge
self.m = mass
self.r = position
Methods: functions tied to a class definition; may operate on data contained by object.
# particle.py
class Particle(object):
"""A particle is a constituent unit of the universe.
Attributes
----------
c : charge in units of [e]
m : mass in units of [kg]
r : position in units of [meters]
"""
roar = "I am a particle!"
def __init__(self, charge, mass, position):
"""Initializes the particle with supplied values for charge c, mass m, and position r.
"""
self.c = charge
self.m = mass
self.r = position
def hear_me(self):
"""Print information about particle.
"""
myroar = self.roar + (
" My charge is: " + str(self.c) +
" My mass is: " + str(self.m) +
" My x position is: " + str(self.r['x']) +
" My y position is: " + str(self.r['y']) +
" My z position is: " + str(self.r['z']))
print(myroar)
Example: proton
Methods can alter instance variables. Example: Quark
class with instance variable flavor
.
def flip(self):
"""Flip quark's flavor to complementary flavor.
"""
if self.flavor == "up":
self.flavor = "down"
elif self.flavor == "down":
self.flavor = "up"
elif self.flavor == "top":
self.flavor = "bottom"
elif self.flavor == "bottom":
self.flavor = "top"
elif self.flavor == "strange":
self.flavor = "charm"
elif self.flavor == "charm":
self.flavor = "strange"
else:
raise AttributeError("The quark cannot be flipped, because the flavor is invalid.")
Particle
capture relationship between uncertainty in momentum and uncertainty in position:
\[ \Delta x \Delta p_x \geq \frac{\hbar}{2} \]
Example: Quark
class can include function that lists all possible values of flavor; possible values are static irrespective of specific instance.
Use @staticmethod
decorator to define a method not bound to object.
“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.”
Meaning: Python does not explicitly check for object types like other languages. It only checks for behavior when a method is called or attribute accessed.
If different object types both “quack like a duck”, then it treats them like a duck. An object does not need to be of a certain type in order for its methods to be invoked; must merely possess those method.
Example: all particles with a valid c
attribute for charge can be used identically.
When class inherits attributes of a parent class. General rule: what works for parent class should work for subclass (plus specialized behavior).
ElementaryParticle
and CompositeParticle
are Particle
objects, and thus have (inherit) all of the functions and data of the original class.
We can override that data and those behaviors if desired.
Any class, including a subclass, can be a superclass or parent class; the subclass inherits from its parent.
ElementaryParticle
can also be a superclass:
Best/most Pythonic way of handling inherited + additional constructor arguments: be explicit.
# particle.py
class Particle(object):
"""A particle is a constituent unit of the universe.
"""
roar = "I am a particle!"
def __init__(self, charge, mass, position):
"""Initializes the particle with supplied values for charge c, mass m, and position r.
"""
self.c = charge
self.m = mass
self.r = position
def hear_me(self):
"""Print information about particle.
"""
myroar = self.roar + (
" My charge is: " + str(self.c) +
" My mass is: " + str(self.m) +
" My x position is: " + str(self.r['x']) +
" My y position is: " + str(self.r['y']) +
" My z position is: " + str(self.r['z']))
print(myroar)
# elementary.py
class ElementaryParticle(Particle):
"""No distinct constituent particles, have spin.
"""
roar = "I am an Elementary Particle!"
def __init__(self, charge, mass, position, spin):
super().__init__(charge, mass, position)
self.s = spin
self.is_fermion = bool(spin % 1.0)
self.is_boson = not self.is_fermion
When definition of a class or function is specified (in part or in full) by code outside the definition itself.
Example: add an is_particle
class attribute to Particle
class:
Take a look at my code PyTeCK, which uses classes to hide lots of details of performing simulations: https://github.com/kyleniemeyer/PyTeCK