What are classes?
Classes are a way of combining information and behavior. For example, let's consider what you'd need to do if you were creating a rocket ship in a game, or in a physics simulation. One of the first things you'd want to track are the x and y coordinates of the rocket. Here is what a simple rocket ship class looks like in code:
class Rocket():
# Rocket simulates a rocket ship for a game or a physics simulation.
def __init__(self):
self.x = 0 # Each rocket has an
self.y = 0 # (x,y)position.
One of the first things you do with a class is to define the _init_() method. The _init_() method sets the values for any parameters that need to be defined when an object is first created. The self part will be explained later; basically, it's a syntax that allows you to access a variable from anywhere else in the class.
The Rocket class stores two pieces of information so far, but it can't do anything. The first behavior to define is a core behavior of a rocket: moving up. Here is what that might look like in code:
class Rocket():
# Rocket simulates a rocket ship for a game or a physics simulation.
def __init__(self):
self.x = 0 # Each rocket has an (x,y) position.
self.y = 0
def move_up(self):
# Increment the y-position of the rocket.
self.y += 1
The Rocket class can now store some information, and it can do something. But this code has not actually created a rocket yet. Here is how you actually make a rocket:
class Rocket():
# Rocket simulates a rocket ship for a game or a physics simulation.
def __init__(self):
self.x = 0 # Each rocket has an (x,y) position.
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)
To actually use a class, you create a variable such as my_rocket. Then you set that equal to the name of the class, with an empty set of parentheses. Python creates an object from the class. An object is a single instance of the Rocket class; it has a copy of each of the class's variables, and it can do any action that is defined for the class. In this case, you can see that the variable my_rocket is a Rocket object from the __main__ program file, which is stored at a particular location in memory.
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:
class Rocket():
# Rocket simulates a rocket ship for a game or a physics simulation.
def __init__(self):
self.x = 0 # Each rocket has an (x,y) position.
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("Rocket altitude:", my_rocket.y)
my_rocket.move_up()
print("Rocket altitude:", my_rocket.y)
my_rocket.move_up()
print("Rocket altitude:", my_rocket.y)
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().
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. 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):
self.x = 0 # Each rocket has an (x,y) position.
self.y = 0
def move_up(self):
# Increment the y-position of the rocket.
self.y += 1
# Create a fleet of 5 rockets, and store them in a list.
my_rockets = []
for x in range(0,5):
new_rocket = Rocket()
my_rockets.append(new_rocket)
# Show that each rocket is a separate object.
for rocket in my_rockets:
print(rocket)
You can see that each rocket is at a separate place in memory. By the way, if you understand list comprehensions, you can make the fleet of rockets in one line:
class Rocket():
# Rocket simulates a rocket ship for a game or a physics simulation.
def __init__(self):
self.x = 0 # Each rocket has an (x,y) position.
self.y = 0
def move_up(self):
# Increment the y-position of the rocket.
self.y += 1
# Create a fleet of 5 rockets, and store them in a list.
my_rockets = [Rocket() for x in range(0,5)]
# Show that each rocket is a separate object.
for rocket in my_rockets:
print(rocket)
What exactly happens in this for loop? The line my_rockets.append(Rocket()) is executed 5 times. Each time, a new Rocket object is created and then added to the list my_rockets. The __init__() method is executed once for each of these objects, so each object gets its own x and y value. When a method is called on one of these objects, the self variable allows access to just that object's attributes, and ensures that modifying one object does not affect any of the other objects that have been created from the class.
Each of these objects can be worked with individually. At this point we are ready to move on and see how to add more functionality to the Rocket class. We will work slowly, and give you the chance to start writing your own simple classes.
You can prove that each rocket has its own x and y values by moving just one of the rockets:
class Rocket(): # Rocket simulates a rocket ship for a game, # or a physics simulation.
def __init__(self):
self.x = 0 # Each rocket has an (x,y) position.
self.y = 0
def move_up(self): # Increment the y-position of the rocket.
self.y += 1 # Create a fleet of 5 rockets, and store them in a list.my_rockets = [Rocket() for x in range(0,5)]
# Move the first rocket up.
my_rockets[0].move_up()
# Show that only the first rocket has moved.
for rocket in my_rockets:
print("Rocket altitude:", rocket.y)
The syntax for classes may not be very clear at this point, but consider for a moment how you might create a rocket without using classes. You might store the x and y values in a dictionary, but you would have to write a lot of ugly, hard-to-maintain code to manage even a small set of rockets. As more features become incorporated into the Rocket class, you will see how much more efficiently real-world objects can be modeled with classes than they could be using just lists and dictionaries.
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:
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
All the __init__() method does so far is set the x and y values for the rocket to 0. We can easily add a couple 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:
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
# 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("Rocket {} is at ({}, {}).".format(index, rocket.x, rocket.y))
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("Rocket {} is at ({}, {}).".format(index, rocket.x, rocket.y))
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.