Attribution
This page originates from TeachBooks/learn-programming, version: mude-2025
import matplotlib
if not hasattr(matplotlib.RcParams, "_get"):
matplotlib.RcParams._get = dict.get
1.2. What are classes?#
Classes can be thought of as blueprints or templates for creating objects.
A class is a body of code that defines the attributes and behaviors required to accurately model something you need for your program. Each class encapsulates both properties (attributes) and functions (methods) related to that type of object.
An attribute is a piece of information. In code, an attribute is just a variable that is part of a class.
A method is an action that is defined within a class, i.e., just a function that is defined for the class.
An object is a particular instance of a class. An object has a certain set of values for all of the attributes (variables) in the class. You can have as many objects as you want for any one class.
Let’s consider an example using a rocket ship in a game. When defining the Rocket class, we have to imagine what are properties and actions that are common to all rockets in this hypotethic game. For example, the a very simple rocket will have some x and y coordinates and will be able to move up. Here is what the rocket class can look like in code:
class Rocket:
# Rocket simulates a rocket ship for a game.
def __init__(self):
# Each rocket has an (x,y) position.
self.x = 0
self.y = 0
def move_up(self):
# Each rocket can move along the y-axis.
self.y += 1
Now let’s examine how we created this class.
The first line, with the keyword class, tells Python that you are about to define a class. The naming convention for classes is the CamelCase, a convention where each letter that starts a word is capitalized, with no underscores in the name. It is recommended that the class name does not have any parentheses after it, unless it inherits from another class (we’ll see this better later).
It is good practice to write a comment at the beginning of your class, describing the class. There is a more formal syntax for documenting your classes, but you can wait a little bit to get that formal. For now, just write a comment at the beginning of your class summarizing what you intend the class to do. Writing more formal documentation for your classes will be easy later if you start by writing simple comments now.
One of the first things you do when creating a class is to define the __init__() method.
The __init__() method is called automatically when you create an object from your class and sets the values for any parameters that need to be defined when an object is first created. 0
We call this method a constructor.
In this case, The __init__() method initializes the x and y values of the Rocket to 0.
The self part is a syntax that allows you to access variables and methods from anywhere else in the class. The word “self” refers to the current object that you are working with. Basically, all methods in a class need the self object as their first argument, so they can access any attribute that is part of the class.
Methods define a specific action that the class can do. A method is just a function that is part of a class. Since it is just a function, you can do anything with a method that you learned about with functions. Each method generally accepts at least one argument, the value self. This is a reference to the particular object that is calling the method.
With this code, we just defined the class, which is a blueprint. To define the actual object, we have to create an instance of the class. In other words, you create a variable that takes all of the attributes and methods as the Rocket class.
This can be done simply as follows:
class Rocket:
# Rocket simulates a rocket ship for a game,
def __init__(self):
# Each rocket has an (x,y) position.
self.x = 0
self.y = 0
def move_up(self):
# Increment the y-position of the rocket.
self.y += 1
# Create a Rocket object.
my_rocket = Rocket()
print(my_rocket)
<__main__.Rocket object at 0x00000206AA7ED340>
Once you have a class, you can define an object and use its methods. Here is how you might define a rocket and have it start to move up:
# Create a Rocket object, and have it start to move up.
my_rocket = Rocket()
print(f"Rocket altitude: {my_rocket.y}", )
my_rocket.move_up()
print(f"Rocket altitude: {my_rocket.y}")
my_rocket.move_up()
print(f"Rocket altitude: {my_rocket.y}")
Rocket altitude: 0
Rocket altitude: 1
Rocket altitude: 2
To access an object’s variables or methods, you give the name of the object and then use dot notation to access the variables and methods.
So to get the y-value of my_rocket, you use my_rocket.y.
To use the move_up() method on my_rocket, you write my_rocket.move_up().
This tells Python to apply the method move_up() to the object my_rocket.
Python finds the y-value associated with my_rocket and adds 1 to that value.
This process is repeated several times, and you can see from the output that the y-value is in fact increasing.
Making multiple objects from a class#
One of the goals of object-oriented programming is to create reusable code. Once you have written the code for a class, you can create as many objects from that class as you need. It is worth mentioning at this point that classes are usually saved in a separate file, and then imported into the program you are working on. So you can build a library of classes, and use those classes over and over again in different programs. Once you know a class works well, you can leave it alone and know that the objects you create in a new program are going to work as they always have.
You can see this “code reusability” already when the Rocket class is used to make more than one Rocket object. Each object is its own instance of that class, with its own separate variables. All of the objects are capable of the same behavior, but each object’s particular actions do not affect any of the other objects. Here is the code that made a fleet of Rocket objects:
class Rocket:
# Rocket simulates a rocket ship for a game,
# or a physics simulation.
def __init__(self):
# Each rocket has an (x,y) position.
self.x = 0
self.y = 0
def move_up(self):
# Increment the y-position of the rocket.
self.y += 1
# Create a fleet of 3 rockets, and store them in a list.
rocket_1 = Rocket()
rocket_2 = Rocket()
rocket_3 = Rocket()
# Show that each rocket is a separate object.
print(rocket_1, rocket_2, rocket_3)
<__main__.Rocket object at 0x00000206AA8DFE90> <__main__.Rocket object at 0x00000206AA8DF650> <__main__.Rocket object at 0x00000206AA8DF830>
You can see that each rocket is at a separate place in memory and therefore printed in a different way.
You can show that each rocket has its own x and y values by moving just one of the rockets:
# Move the third rocket up.
rocket_3.move_up()
# Show that only the third rocket has moved.
print(f"Rocket altitudes: {rocket_1.y}, {rocket_2.y}, {rocket_3.y}")
Rocket altitudes: 0, 0, 1
A quick check-in#
If all of this makes sense, then the rest of your work with classes will involve learning a lot of details about how classes can be used in more flexible and powerful ways. If this does not make any sense, you could try a few different things:
Reread the previous sections, and see if things start to make any more sense.
Type out these examples in your own editor, and run them. Try making some changes, and see what happens.
Read on. The next sections are going to add more functionality to the Rocket class. These steps will involve rehashing some of what has already been covered, in a slightly different way.
Classes are a huge topic, and once you understand them you will probably use them for the rest of your life as a programmer. If you are brand new to this, be patient and trust that things will start to sink in.
Adding parameters to the class#
The Rocket class so far is very simple. It can be made a little more interesting with some refinements to the __init__() method, and by the addition of some methods.
Accepting parameters for the __init__() method#
The __init__() method is run automatically one time when you create a new object from a class. The __init__() method for the Rocket class so far is pretty simple.
But we can easily add keyword arguments so that new rockets can be initialized at any position:
class Rocket:
# Rocket simulates a rocket ship for a game,
# or a physics simulation.
def __init__(self, x=0, y=0):
# Each rocket has an (x,y) position.
self.x = x
self.y = y
def move_up(self):
# Increment the y-position of the rocket.
self.y += 1
Now when you create a new Rocket object you have the choice of passing in arbitrary initial values for x and y:
# Make a series of rockets at different starting places.
rockets = []
rockets.append(Rocket())
rockets.append(Rocket(0,10))
rockets.append(Rocket(100,0))
# Show where each rocket is.
for index, rocket in enumerate(rockets):
print(f"Rocket {index} is at (x,y)=({rocket.x}, {rocket.y}).")
Rocket 0 is at (x,y)=(0, 0).
Rocket 1 is at (x,y)=(0, 10).
Rocket 2 is at (x,y)=(100, 0).
Accepting parameters in a method#
The __init__() method is just a special method that serves a particular purpose, which is to help create new objects from a class. Any method in a class can accept parameters of any kind. With this in mind, the move_up() method can be made much more flexible. By accepting keyword arguments, the move_up() method can be rewritten as a more general move_rocket() method. This new method will allow the rocket to be moved any amount, in any direction:
class Rocket():
# Rocket simulates a rocket ship for a game,
# or a physics simulation.
def __init__(self, x=0, y=0):
# Each rocket has an (x,y) position.
self.x = x
self.y = y
def move_rocket(self, x_increment=0, y_increment=1):
# Move the rocket according to the paremeters given.
# Default behavior is to move the rocket up one unit.
self.x += x_increment
self.y += y_increment
The paremeters for the move() method are named x_increment and y_increment rather than x and y. It’s good to emphasize that these are changes in the x and y position, not new values for the actual position of the rocket. By carefully choosing the right default values, we can define a meaningful default behavior. If someone calls the method move_rocket() with no parameters, the rocket will simply move up one unit in the y-direciton. Note that this method can be given negative values to move the rocket left or right:
class Rocket():
# Rocket simulates a rocket ship for a game,
# or a physics simulation.
def __init__(self, x=0, y=0):
# Each rocket has an (x,y) position.
self.x = x
self.y = y
def move_rocket(self, x_increment=0, y_increment=1):
# Move the rocket according to the paremeters given.
# Default behavior is to move the rocket up one unit.
self.x += x_increment
self.y += y_increment
# Create three rockets.
rockets = [Rocket() for x in range(0,3)]
# Move each rocket a different amount.
rockets[0].move_rocket()
rockets[1].move_rocket(10,10)
rockets[2].move_rocket(-10,0)
# Show where each rocket is.
for index, rocket in enumerate(rockets):
print(f"Rocket {index} is at ({rocket.x}, {rocket.y}).")
Rocket 0 is at (0, 1).
Rocket 1 is at (10, 10).
Rocket 2 is at (-10, 0).
Adding a new method#
One of the strengths of object-oriented programming is the ability to closely model real-world phenomena by adding appropriate attributes and behaviors to classes. One of the jobs of a team piloting a rocket is to make sure the rocket does not get too close to any other rockets. Let’s add a method that will report the distance from one rocket to any other rocket. Note how this method uses another instance of the same class as one of its arguments!
If you are not familiar with distance calculations, there is a fairly simple formula to tell the distance between two points if you know the x and y values of each point. This new method performs that calculation, and then returns the resulting distance.
from math import sqrt
class Rocket:
# Rocket simulates a rocket ship for a game,
# or a physics simulation.
def __init__(self, x=0, y=0):
# Each rocket has an (x,y) position.
self.x = x
self.y = y
def move_rocket(self, x_increment=0, y_increment=1):
# Move the rocket according to the paremeters given.
# Default behavior is to move the rocket up one unit.
self.x += x_increment
self.y += y_increment
def get_distance(self, other_rocket):
# Calculates the distance from this rocket to another rocket,
# and returns that value.
distance = sqrt((self.x-other_rocket.x)**2+(self.y-other_rocket.y)**2)
return distance
# Make two rockets, at different places.
rocket_0 = Rocket()
rocket_1 = Rocket(10,5)
# Show the distance between them.
distance = rocket_0.get_distance(rocket_1)
print(f"The rockets are {distance:.6f} units apart.")
The rockets are 11.180340 units apart.
Hopefully these short refinements show that you can extend a class’ attributes and behavior to model the phenomena you are interested in as closely as you want. The rocket could have a name, a crew capacity, a payload, a certain amount of fuel, and any number of other attributes. You could define any behavior you want for the rocket, including interactions with other rockets and launch facilities, gravitational fields, and whatever you need it to! There are techniques for managing these more complex interactions, but what you have just seen is the core of object-oriented programming.