Python OOP Classes & Objects Tutorial

In this tutorial we learn how to encapsulate data and behavior into standalone units with classes. We explain more about classes and the objects instantiated from them.

We also cover class constructors and how to refer to the calling object with the keyword self. Finally, we take a quick look at attribute and method members inside classes.


note Object Oriented Programming is a difficult topic for new programmers to understand. It’s okay if this section of the tutorial series takes you longer to understand than the others.

Object Oriented Programming, Classes and Objects

One of the most popular approaches to problem solving, in most programming languages, is objected oriented programming, or OOP for short.

OOP is a way to treat data and behavior as a single, reusable unit. Essentially, we attempt to model our application after entities in the real world by using classes and objects.

What is a class?

A class is a blueprint, or recipe for an object. A class defines what every instance of it should contain. As an example, let’s consider a pizza.

Every pizza has:

  • A base
  • A sauce
  • Cheese
  • Toppings

This is our recipe, our class for a pizza. Not every pizza (instance of our class) will have the same toppings, but it will have toppings.

Classes may contain attributes, which are similar to keys in a dictionary, as well as methods, which are similar to functions. As an example, let’s consider a database of users.

Every user has attributes like:

  • A name
  • An age
  • An address
  • A profile picture
  • An email
  • A password etc.

Every user has functionality available to them, like:

  • Change password
  • Upload profile picture
  • Add to cart
  • Logout etc.

We combine our attributes and functionality into this single entity, a class. Every instance of a user will follow the pattern set up in the class but with different attributes.

What is an instance or object

When we use the term instance, or object, we are referring to an instance of a class. A single user, or pizza, that is created from the class blueprint.

Think of a class as our own type, like int or list is a type.

When we create a list, we are actually creating an instance object of the List class.

Example:
# object of list class
shopping_list = ["Bread", "Milk"]

The List class has the append() function defined inside it. So once we have an object instance (“shopping_list”), we can use the function to add an item to it.

Example:
# object of list class
shopping_list = ["Bread", "Milk"]

shopping_list.append("Cheese")

Any list object we create will have access to append() and can use it to add items to its list.

Example:
shopping_list = ["Bread", "Milk"]
shopping_list.append("Cheese")

todo_list = ["Walk dog", "Wash car"]
todo_list.append("Teach dog to wash car")

How to create a class

We create a class by using the class keyword and a name, immediately followed by a colon.

Syntax:
class ClassName:
    # attributes
    # methods
Example:
class Person:
    # attributes
    name = "Unknown"

    # methods (functions)
    def walk():
        print("Walking...")

We should be aware of two conventions typically used when naming classes:

  1. We name our classes in the singular. Instead of a class named “Cars”, we call it “Car”.
  2. We use Pascal casing. The first letter of the name is uppercase and each new word in the name has its first letter in uppercase. We don’t separate words with underscores.

We don’t have to name our classes this way but it is a good convention to follow. Some enterprises may require you to follow conventions such as these, or their own.

In this tutorial series we will be using the PascalCase convention for classes.

How to instantiate an object instance of a class

To be able to use the class we would need to create an instance of it. We do this in the same way as we would with a function, by assigning the class to a data container such as a variable.

Syntax:
class ClassName:
    # attributes
    # methods

object_name = ClassName()
Example:
class Person:
    # attributes
    name = "Unknown"

    # methods
    def walk():
        print("Walking...")

john = Person()

In this case, the object is called “john” and is an instance of the Person class.

It has access to the name attribute and the walk() method.

We can confirm that “john” is an object of Person by printing the object to the console.

Example:
class Person:
    # attributes
    name = "Unknown"

    # methods
    def walk():
        print("Walking...")

john = Person()
print(john)

When we print the object name, we will receive the following output.

Output:
<__main__.Person object at 0x00E0DD90>

Process finished with exit code 0

This means that the object we printed, “john”, is an object of the Person class. The 0x00E0DD90 is the memory address the object lives at in the RAM.

Class constructor (The __init__() method)

When the class is instantiated as an object, it automatically calls a built-in method called __init__() , otherwise known as a class constructor.

It takes care of some necessary operations behind the scenes when the object is created and we can use it to initialize attributes with values.

We specify the attributes we want declared as parameters of the __init__() method. Then we assign those parameter values to attributes.

tip We refer to attributes inside the __init__() method as instance attributes.

Syntax:
class ClassName:
    def __init__(self, attribute_1, attribute_2, ...):
        self.attribute_1 = attribute_1
        self.attribute_2 = attribute_2

Let’s say we want each Person instance to declare their name and age when we instantiate them, instead of just “Unknown”.

Example:
class Person:
    # instanced attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

This allows us to use Person as a function when we instantiate an object and pass the values for “name” and “age” as arguments.

Example:
class Person:
    # instanced attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

user_1 = Person("John", 20)
user_2 = Person("Jane", 20)

In the example above we give each user a name and an age, like we would in a function. The values we pass into the class gets taken by the method and are declared as attributes.

How to refer to the calling object with self

The self parameter refers to the current object instance. We use it to access instanced attributes that belong to the class.

tip If you’re familiar with other general programming languages such as C#, the self parameter would be similar to this .

Any time we want to refer to an attribute defined inside the class, we prefix it with self followed by a . (dot operator).

Example:
class Person:

    def __init__(self):
        # This object's name
        self.name = "John"

    def print_name(self):
        # Access this object's name
        print(self.name)

user_1 = Person()
user_1.print_name()

In the example above, we want to refer to the “name” attribute inside the class method so we use self.name .

note Because we use self in the method, we have to pass it as a parameter to have access to it inside the method.

If we don’t use self , the interpreter will think we’re trying to access a “name” defined outside the class. And because there isn’t one, it would raise a NameError.

Output:
 NameError: name 'name' is not defined

Self parameter notes

There are a few things we should keep in mind when using the self parameter.

  • The self parameter must always be present in instanced methods inside a class.
  • The self parameter must always be first in the list of parameters.
  • We are allowed to rename self to whatever we want. However, it’s considered bad practice and may lead to confusing code.

How to access instanced attributes & methods

To access the instanced attributes and members inside a class we use dot notation.

The dot operator essentially tells the interpreter to go look inside the class that this object is based on, find the instanced attribute or method and do something with it.

We write the object name, immediately followed by the dot operator and the attribute or method we want to use.

Syntax:
# attributes
object_name.attribute

# methods
object_name.method()
Example:
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_name(self):
        print("Hello, my name is", self.name)

# instantiate objects
user_1 = Person("John", 20)
user_2 = Person("Jane", 20)

# access method
user_1.print_name()
user_2.print_name()

How to change attribute values

We can change an attribute’s value at any time, all we need to do is assign a new value to it with the assignment operator.

Syntax:
 object_name.attribute = new_value
Example:
class Person:
    # instanced attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_name(self):
        print("Hello, my name is", self.name)

# instantiate object
user_1 = Person("John", 20)

# access method
user_1.print_name()

# change value
user_1.name = "Jack"

# access method
user_1.print_name()

How to delete instanced attributes from objects

We can delete instanced attributes from an object with the del keyword.

Syntax:
 del object_name.instance_attribute
Example:
class Person:
    # instanced attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_name(self):
        print("Hello, my name is", self.name)

# instantiate object
user_1 = Person("John", 20)

# delete object attribute
del user_1.name

# access method
user_1.print_name()

Because we delete the name instanced attribute, the example above will raise an AttributeError.

Output:
AttributeError: 'Person' object has no attribute 'name'

note The instanced attribute is not deleted from the class, only from the object instance.

Example:
class Person:
    # instanced attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_name(self):
        print("Hello, my name is", self.name)

# instantiate object
user_1 = Person("John", 20)
user_2 = Person("Jane", 20)

# delete object attribute
del user_1.name

# access method
user_2.print_name()

In the example above we delete the name instanced attribute from “user_1” but not “user_2”, so we can still use it in “user_2”.

How to delete an object instance

We can also delete a whole object with the del keyword.

Syntax:
 del object_name
Example:
class Person:
    # instanced attributes
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_name(self):
        print("Hello, my name is", self.name)

# instantiate object
user_1 = Person("John", 20)

# delete object
del user_1

# access method
user_1.print_name()

Because we deleted the “user_1” object the interpreter will raise a NameError.

Output:
 NameError: name 'user_1' is not defined

The object doesn’t exist anymore so the interpreter couldn’t find it to use the “print_name()” method.

Summary: Points to remember

  • Object Oriented Programming is a way to structure and think about our code, it’s not a requirement.
  • A class is a blueprint for an object.
  • An object is an instance of a class.
  • We must use self to refer to members of the object instance.
    • self must be specified in a method’s parameter list.
    • self must be the first parameter in a method’s parameter list.
  • Classes are named in PascalCase.