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.

Here's a table of contents of what you'll learn in this lesson:
(click on a link to skip to its section)

Let's jump right in.

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, or object, of the list class.

Once we have an object, we can access the properties and functions that Python declared inside the class.

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

shopping_list.append("Cheese")

We can create many of these list instances, each with their own values, and each containing the append() method.

Even though we didn’t know much about classes, we’ve been using them throughout the tutorial series so far.

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
	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()

Our object in this case is called “john”, “john” is an instance of the Person class. “John” has a name (even though it currently shows as Unknown), and “john” can walk because “john” has the attributes and functionality defined in the Person class.

We can confirm that “john” is an object of Person by using print.

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 always calls a built-in method called __init__() , otherwise known as a class constructor.

The __init__() method takes care of some necessary operations behind the scenes when the object is created.

We can use the __init__() method to allow us to initialize attributes with values when we instantiate the object.

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

We can use the __init__() method to specify the attributes we want declared on instantiation.

Syntax:
class ClassName:
	def __init__(self, attribute_1, attribute_2, ...):
		self.attribute_1 = attribute_1
		self.attribute_2 = attribute_2
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 parameters.

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

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 __init__() method and are declared as attributes.

How to refer to the calling object with self

The self parameter refers to the instance itself and we use it to access instanced attributes that belong to the class. We can also use it to access class attributes, but we will talk more about class attributes later.

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

As an example, let’s try to use an instance attribute inside a function.

Example:
class Person:

	def __init__(self):
		# instance attribute
		self.name = "John"

	def print_name(self):
		# variable local to method
		if name == "Unknown":
			print("Name has not been specified")

user_1 = Person()

# Access print_name method
user_1.print_name()

If we were not inside the clas,s we would have been able to use “name” directly inside the print_name() function.

However, because we’re inside a class, we have to specify that we want to use the “name” attribute from inside the class, and not from a name variable somewhere else in our code.

The example above would raise a NameError.

Output:
 NameError: name 'name' is not defined

In the print_name() method the interpreter will assume that we want a variable that was defined inside the method, or from outside of the class itself. No such variable exists so it will raise an error.

If we were to create the “name” variable outside of the class it would use that.

Example:
name = "Name defined outside of the class"

class Person:

	def __init__(self):
		# instance attribute
		self.name = "John"

	def print_name(self):
		if name == "Unknown":
			print("Name has not been specified")
		else:
			print(name)

user_1 = Person()

# Access print_name method
user_1.print_name()

Or if we created a name variable inside the method, it would use that.

Example:
name = "Name defined outside of the class"

class Person:

	def __init__(self):
		# instance attribute
		self.name = "John"

	def print_name(self):
		name = "Name inside class method"

		if name == "Unknown":
			print("Name has not been specified")
		else:
			print(name)

user_1 = Person()

# Access print_name method
user_1.print_name()

So, we need the self parameter to indicate that we want to use the instanced attribute inside the class.

Example:
name = "Name defined outside of the class"

class Person:
	# attributes
	name = "Unknown"

	def print_name(self):
		name = "Name inside class method"

		print(self.name)

user_1 = Person()

# Access print_name method
user_1.print_name()
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 parameter 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'

It’s important to note that 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” and 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.