Object-oriented programming (OOP) is one of the most effective approaches to writing software. In OOP you write classes that represent real-world things and situations, and you create objects based on these classes. When you write a class, you define the general behavior that a whole category of objects can have. When you create individual objects from the class, each object is automatically equipped with the genearl behavior; you can then give each object whatever unique traits you desire. You'll be amazed how well real-world situations can be modeled with object-oriented programming.
Making an object from a class is called instantiation, and you work with instances of a class. On this page, we'll write classes and create instances of those classes. We will specify the kind of information that will be stored in instances, and we'll define actions that can be taken with these instances. We'll also write classes that extend the functionality of existing classes, so similar classes can share code efficiently. We'll store our classes in modules and import classes written by other programmers into our program files.
Understanding object-oriented programming will help us see the world as a programmer does. It'll help us really know our code, not just what's happening line by line, but also the bigger picture behind it. Knowing the logic behind classes will train us to think logically so we can write programs that effectively address almost any problem we encounter.
Creating and Using a Class
You can model almost anything using classes. Let's start by writing a simple class,
Dog, that represents a dog - not one dog in particular, but any dog. What do we know about pet dogs? Well, they all have a name and age. We also know that most dogs sit and roll over. Those two pieces of information (name and age) and those two behaviors (sit and roll over) will go in our
Dog class because they're common to most dogs. This class will tell Python how to make an object representing a dog. After our class is written, we'll use it to make individual instances, each of which represent one specific dog.
Creating the Dog Class
Each instance created from the
Dog class will store a
name and an
age, and we'll give each dog the ability to
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Dog. By convention, capitalized names refer to classes in Python. There are no parenthesis in the class deinition because we're creating this class from scratch. On line 2, we write a docstring describing what this class does.
A function that's part of a class is a method. Everything you learned about functions applies to methods as well; the only practical difference for now is the way we'll call methods. The
__init__() method on line 4 is a special method that Python runs automatically whenever we create a new instance based on the
Dog class. This method has two leading underscores and two trailing underscores, a convention that helps prevent Python's default method names from conflicting with your method names. Make sure to use two underscores on each side of
__init__(). If you use just one on each side, the method won't be called automatically when you use your class, which can result in errors that are difficult to identify.
We define the
__init__() method to have three parameters:
self parameter is required in the method definition, and it must come first before the other parameters. It must be included in the definition because when Python cals this method later (to create an instance of
Dog), the method call will automatically pass the
self argument. Every method call associated with an instance automatically passes
self, which is a reference to the instance itself; it gives the individual instance access to the attributes and methods in the class. When we make an instance of
Dog, Python will call the
__init__() method from the
Dog class. We'll pass
Dog() a name and an age as arguments; self is passed automatically, so we don't need to pass it. Whenever we want to make an instance from the
Dog class, we'll provide values for only the last two parameters,
The two variables defined on line 6/7 each have the prefix
self. Any varibale prefixed with
self is available to every method in the class, and we'll also be able to access these variables through any instance created from the class. The line
self.name = name takes the value associated with the parameter
name and assigns it to the variable
name, which is then attached to the instance being created. The same process happens with
self.age = age. Variables that are accessible through instances like this are called attributes.
Dog class has two other methods defined:
roll_over() (line 9 & 13). Because these methods don't need additional information to run, we just define them to have one parameter,
self. The instances we create later will have access to these methods. In other words, they'll be able to sit and roll over. For now,
roll_over() don't do much. They simply print a message saying the dog is sitting or rolling over. But the concept can be extended to realistic situations: if this class were part of an actual computer game, these messages would contain code to make an animated dog sit and roll over. If this class was written to control a robot, these methods would direct movements that cause a robotic doc to sit and roll over.
Making an Instance from a Class
Think of a class as a set of instructions for how to make an instance. The class
Dog is a set of instructions that tells Python how to make individual instances representing specific dogs.
Let's make an instance representing a specific dog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Dogclass we're using here is the same from the previous example. On line 17 we tell Python to create a dog whose name is
'Willie'and whose age is
6. When Python reads this line, it calls the
Dogwith the arguments
__init__()method creates an instance representing this particular dog and sets the
ageattributes using the values we provided. Python then returns an instance representing this dog. We assign that instance to the variable
my_dog. The naming convention is helpful here: we can usually assume that a capitalized name like
Dogrefers to a class, and a lowercase name like
my_dogrefers to a single instance created from a class.
To acces the attributes of an instance, you use dot notation. On line 19, we access the value of
my_dog's name by writing:
my_dogand then finds the attribute
my_dog. This is the same attribute referred to as
self.namein the class
Dog. On line 20 we use the same approach to work with the attribute
The output is a summary of what we know about
My dog's name is Willie. My dog is 6 years old.
After we create an instance from the class
Dog, we can use dot notation to call any method defined in
Dog. Let's make our dog sit and roll over:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
my_dog) and the method you want to call, separated by a dot. When Python reads
my_dog.sit(), it looks for the method
sit()in the class
Dogand runs that code. Python interprets the line
my_dog.roll_over()in the same way.
Now Willie does what we tell him to:
Willie is now sitting. Willie rolled over!
roll_over(), we can easily infer what a block of code, even one we've never seen before, is supposed to do.
Creating Multiple Instances
You can create as many instances from a class as you need. Let's create a second dog called
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
My dog's name is Willie. My dog is 6 years old. Willie is now sitting. Your dog's name is Lucy. Your dog is 3 years old. Lucy is now sitting.
Dogclass. You can make as many instances from one class as you need, as long as you give each instance a unique variable name or it occupies a unique spot in a list or dictionary.
Working with Classes and Instances
You can use classes to represent many real-word situations. Once you write a class, you'll spend most of your time working with instances created from that class. One of the first tasks you'll want to do is modify the attributes associated with a particular instance. You can modify the attributes of an instance directly or write methods that update attributes in specific ways.
The Car Class
Let's write a new class representing a car. Our class will store information about the kind of car we're working with, and it will have a method that summarizes this information:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Carclass, we define the
__init__()method with the
selfparameter first, just like we did before with our
Dogclass. We also give in three other parameters:
__init__()method takes in these parameters and assigns them to the attributes that will be associated with instances made from this class. When we make a new
Carinstance, we'll need to specify a make, model, and year for our instance.
On line 10 we define a method called
get_descriptive_name() that puts a car's
model into one string neatly describing the car. This will spare us from having to print each attribute's value individually. To work with the attribute values in this method, we use
self.year. On line 15 we make an instance from the
Car class and assign it to the variable
my_new_car. Then we call
get_descriptive_name() to show what kind of car we have:
2020 Audi A4
Setting a Default Value for an Attribute
When an instance is created, attributes can be defined without being passed in as parameters. These attributes can be defined in the
__init__() method, where they are assigned a default value.
Let's add an attribute called
odometer_reading that always starts with a value of
0. We'll also add a method
read_odometer() that helps us read each car's odometer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
__init__()method to create a new instance, it stores the make, model, and year values as attributes like it did in the previous example. Then Python creates a new attribute called
odometer_readingand sets its initial value to
0(line 9). We also have a new method called
read_odometer()on line 16 that makes it easy to read a car's mileage.
Our car starts with a mileage of 0:
2020 Audi A4 This car has 0 miles on it.
Modifying Attribute Values
You can change an attribute's value in three ways:
Modifying an Attribute's Value Directly
The simplest way to modify the value of an attribute is to access the attribute directly through an instance. Here we set the odometer reading to 23 directly:
1 2 3 4 5 6 7 8
odometer_readingattribute and set its value directly. This line tells Python to take the instance
my_new_car, find the attribute
odomerter_readingassociated with it, and set the value of that attribute to 23:
2020 Audi A4 This car has 23 miles on it.
Modifying an Attribute's Value Through a Method
It can be helpful to have methods that update certain attributes for you. Instead of accessing the attribute directly, you pass the new value to a method that handles the updating internally.
Here's an example showing a method called
1 2 3 4 5 6 7 8 9 10 11 12
Caris the addition of
update_odometer()on line 4. This method takes in a mileage value and assigns it to
self.odometer_reading. On line 11 we call
update_odometer()and give it 23 as an argument (corresponding to the
mileageparameter in the method definition). It sets the odometer reading to
read_odometer()prints the reading:
2020 Audi A4 This car has 23 miles on it.
update_odometer()to do additional work every time the odometer reading is modified. Let's add a little logic to make sure no one tries to roll back the odometer reading:
1 2 3 4 5 6 7 8 9 10 11 12
update_odometer()checks that the new reading makes sense before modifying the attribute. If the new mileage,
mileage, is greater than or equal to the existing mileage,
self.odometer_reading, you can update the odometer reading to the new mileage (line 9). If the new mileage is less than the existing mileage, you'll get a warning that you can't roll back an odometer (line 12).
Incrementing an Attribute's Value Through a Method
Sometimes you'll want to increment an attribute's value by a certain amount rather than set an entirely new value. Say we buy a used car and put 100 miles on it between the time we buy it and the time we register it. Here's a method that allows us to pass this incremental amount and add that value to the odometer reading:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
increment_odometer()at line 7 takes in a number of miles and adds this value to
self.odometer_reading. On line 11 we create a used car,
my_used_car. We set its odomoer to
update_odometer()and passing it
23_500on line 14. On line 16 we call
increment_odometer()and pass it
100to add the 100 miles that we drove between buying the car and registering it:
2015 Subaru Outback This car has 23500 miles on it. This car has 23600 miles on it.
You don't always have to start from scratch when writing a class. If the class you're writing is a specialized version of another class you wrote, you can use inheritance. When one class inherits from another, it takes on the attributes and methods of the first class. The original class is called the parent class, and the new class is the child class. The child class can inherit any or all of the attributes and methods of its parent class, but it's also free to define new attributes and methods of its own.
__init__() Method for a Child Class
When you're writing a new class based on an existing class, you'll often want to call the
__init__() method from the parent class. This will initialize any attributes that were defined in the parent
__init__() method and make them available in the child class.
As an example, let's model an electric car. An electric car is just a specific kind of car, so we can base our new
ElectricCar class on the
Car class we wrote earlier. Then we'll only have to write code for the attributes and behavior specific to electric cars.
Let's start by making a simple version of the
ElectricCar class, which does everythin the
Car class does:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
Car. When you create a child class, the parent class must be part of the current file and must appear before the child class in the file. On line 25 we define the child class,
ElectricCar. The name of the parent class must be included in the parentheses in the defintion of a child class. The
__init__()method on line 28 takes in the information required to make a
super() function on line 30 is a special function that allows you to call a method from the parent class. This line tells Python to call the
__init__() method from
Car, which gives and
ElectricCar instance all the attributes fefined in that method. The name super comes from a convention of calling the parent class a superclass and the child class a subclass.
We test whether inheritence is working properly by trying to create an electric car with the same kind of information we'd provide when making a regular car. On line 32 we make an instance of the
ElectricCar class and assign it to
my_tesla. This line calls the
__init__() method defined in
ElectricCar, which in turn tells Python to call the
__init__() method defined in the parent class
Car. We provide the arguments
'model s', and
__init__(), there are no attributes or methods yet that are particular to an electric car. At this point we're just making sure the car has the appropriate
2019 Tesla Model S
ElectricCarinstance works just like an instance of
Car, so now we can begin defining attributes and methods specific to electric cars.
Defining Attributes and Methods for the Child Class
Once you have a child class that inherits from a parent class, you can add any new attributes and methods necessary to differentiate the child class from the parent class.
Let's add an attribute that's specific to electric cars (a battery, for example) and a method to report on this attribute. We'll store the battery size and write a method that print a description of the battery:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
self.battery_sizeand set its initial value to
75. This attribute will be associated with all instances created from the
ElectricCarclass but won't be associated with any instances of
Car. We also add a method called
describe_battery()that prints information about the battery on line 15. When we call this method, we get a description that is clearly specific to an electric car:
2019 Tesla Model S This car has a 75-kWh battery.
ElectricCarclass. You can add as many attributes and methods as you need to model an electric car to whatever degree of accuracy you need. An attribute or method that could belong to any car, rather than one that's specific to an electric car, should be added to the
Carclass instead of the
ElectricCarclass. Then anyone who uses the
Carclass will have that functionality available as well, and the
ElectricCarclass wil only contain code for the information and behavior specific to electric vehicles.
Overriding Methods from the Parent Class
You can override any method from the parent class that doesn't fit what you're trying to model with the child class. To do this, you define a method in the child class with the same name as the method you want to override in the parent class. Python will disregard the parent class method and only pay attention to the method you define in the child class.
Say the class
Car had a method called
fill_gas_tank(). This method is meaningless for an all-electric vehicle, so you might want to override this method. Here is one way to do that:
1 2 3 4 5 6
Now if someone tries to call
fill_gas_tank() with an electric car, Python will ignore the method
Car and run this code instead. When you use inheritence, you can make your child classes retain what you need and override anything you don't need from the parent class.
Instances as Attributes
When modeling something from the real world in code, you may find that you're adding more and more detail to a class. You'll find that you have a growing list of attributes and methods and that your files are becoming lengthy. In these situations, you might recognize that part of one class can be written as a separate class. You can break your large class into smaller classes that work together.
For example, if we continue adding detail to the
ElectricCar class, we might notice that we're adding many attributes and methods specific to the car's battery. When we see this happening, we can stop and move those attributes and methods to a separate class called
Battery. Then we can use a
Battery instance as an attribute in the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
class Batterythat doesn't inherit from any other class. The
__init__()method on line 7 has one parameter
battery_size, in addition to
self. This is an optional parameter that sets the battery's size to
75if no value is provided. The method
describe_battery()has been moved to this class as well (line 11).
ElectricCar class, we now add an attribute called
self.battery (line 24). This line tells Python to create a new instance of
Battery (with a default size of
75 because we're not specifying a value) and assign that instance to the attribute
self.battery. This will happen every time the
__init__() method is called; and
ElectricCar instance will now have a
Battery instance created automatically.
We create an electric car and assign it to the variable
my_tesla. When we want to describe the battery, we need to work through the car's
my_tesla, find its
batteryattribute, and call the method
describe_battery()that's associated with the
Batteryinstance stored in the attribute.
The output is identical to what we saw previously:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
get_range()on line 7, performs some simple analysis. If the battery's capacity is
get_range()sets the range to
260miles, and if the capacity is
100kWh, it sets the range to
315miles. It then reports this value. When we want to use this method, we again have to call it through the car's
batteryattribute on line 21.
The output tells us the range of the car based on its battery size:
2019 Tesla Model S This car has a 75-kWh battery. This car can go about 260 mile son a full charge.