Python • Object-Oriented Programming

Defining Classes — Define classes and create instances

Learn how to design your own data types using Python classes and bring them to life as objects.

Code braces with terminal and markup symbols representing Python programming.
Inside this lesson 10 sections 5 questions 5 figures

SECTION 1

Historical Context & Motivation

Before object-oriented programming (OOP) existed, programmers wrote long sequences of instructions that acted on separate data stored in variables. As programs grew larger, this approach became difficult to manage. Imagine trying to track every student in a school using hundreds of individual variables like student1_name, student1_grade, student2_name, and so on. The code would quickly become unreadable and error-prone. Programmers needed a way to bundle related data and behavior together into a single, reusable package.

1967 • Simula — The First OOP Language

Norwegian computer scientists Ole-Johan Dahl and Kristen Nygaard created Simula, introducing the concepts of classes and objects for the first time in a programming language.

1972 • Smalltalk Refines OOP

Alan Kay and his team at Xerox PARC developed Smalltalk, a language where everything is an object. This solidified OOP as a major programming paradigm.

1991 • Python Is Released

Guido van Rossum released Python, a language built from the ground up to support OOP with a clean, readable syntax that makes defining classes straightforward.

2000s • OOP Becomes Standard Practice

Object-oriented programming became the dominant approach in software engineering, used in web development, game design, data science, and more.

The central question that OOP answers is this: how can we organize code so that data and the operations on that data stay together, making programs easier to write, understand, and maintain? The answer lies in classes and instances — the fundamental building blocks of object-oriented Python.

SECTION 2

Core Principles & Definitions

To understand classes, you first need to grasp a few key ideas. A class is a blueprint or template that defines what data an object holds and what actions it can perform. An instance (also called an object) is a specific realization of that blueprint, created with its own unique data. Think of a class as a cookie cutter and each instance as an individual cookie — they share the same shape, but each can be decorated differently.

1

Class

A blueprint that defines attributes (data) and methods (functions) that its objects will have. Defined using the class keyword.
2

Instance (Object)

A specific object created from a class. Each instance has its own copy of the class's attributes. Created by calling the class name like a function: my_dog = Dog().
3

The __init__ Method

A special method called the constructor. It runs automatically when a new instance is created, setting up the object's initial attributes.
4

The self Parameter

The first parameter of every method in a class. It refers to the specific instance calling the method, letting each object access its own data.
5

Attributes & Methods

Attributes are variables that belong to an object. Methods are functions defined inside a class that describe the object's behavior.
✦

KEY TAKEAWAY

Think of a class like a blueprint for a house. The blueprint itself isn't a house — it's a set of plans that describes the layout, rooms, and features. When you actually build a house from that blueprint, that's an instance. You can build many houses from one blueprint, and each house can have different paint colors and furniture, just like each instance can hold different data.

SECTION 3

Visual Explanation — From Class to Instance

From Class Blueprint to Object InstancesCLASS: Dog__init__(self, name, breed):self.name = nameself.breed = breedself.energy = 100Methods:bark(self)fetch(self, item)rest(self)Blueprint (template)Dog()Dog()Dog()Instance: buddyname = "Buddy"breed = "Golden Retriever"energy = 100Instance: rexname = "Rex"breed = "German Shepherd"energy = 100Instance: tinyname = "Tiny"breed = "Chihuahua"energy = 100
The purple box on the left represents the Dog class — the blueprint. The three boxes on the right are instances created from that blueprint, each holding its own unique data for name and breed.

In the diagram above, notice that all three instances share the same structure — they each have name, breed, and energy attributes — because they were all created from the same class. However, the actual values stored differ. This is the power of classes: you define the structure once, then create as many unique objects as you need.

SECTION 4

How It Works — Anatomy of a Class Definition

Let's break down the exact syntax Python uses to define a class. Every class definition follows a consistent pattern: the class keyword, the class name (by convention in PascalCase), a colon, and then an indented block containing the __init__ method and any other methods.

The class Keyword and Naming

You begin every class definition with the class keyword followed by the name of the class. Python convention says class names should use PascalCase — each word starts with a capital letter, with no underscores. For example, StudentRecord rather than student_record. This helps you visually distinguish classes from regular functions and variables.

The __init__ Constructor

The __init__ method is where you set up an object's initial state. Python calls this method automatically every time you create a new instance. Its first parameter is always self, which refers to the newly created object. Any additional parameters become the data you pass in when creating the object. Inside __init__, you assign attributes using self.attribute_name = value.

Defining Methods

Methods are functions defined inside a class that describe what an object can do. Every method must include self as its first parameter so it can access the object's attributes. When you call a method on an instance — like buddy.bark() — Python automatically passes the instance as the self argument behind the scenes.

Creating Instances

To create an instance, you call the class name as if it were a function, passing the arguments that __init__ expects (excluding self). For example, buddy = Dog("Buddy", "Golden Retriever") creates a new Dog object and assigns it to the variable buddy. This process is called instantiation.

💡

Remember: self is Automatic

When you define a method like def bark(self): you write self in the definition, but you never pass it explicitly when calling. You just write buddy.bark() — Python fills in self for you.

SECTION 5

Detailed Breakdown — Inside a Complete Class

Annotated Class Definitionclass Student:def __init__(self, name, grade):self.name = nameself.grade = gradeself.gpa = 0.0self.courses = []def enroll(self, course):self.courses.append(course)def introduce(self):return f"Hi, I'm {self.name}"# Creating instancesalice = Student("Alice", 10)bob = Student("Bob", 11)① Class name (PascalCase)② Constructor (__init__)③ Instance attributes (self.x)④ Custom methods⑤ Instantiation (creating objects)
This annotated code view shows a complete Student class with labeled callouts: ① the class name, ② the constructor, ③ instance attributes set via self, ④ custom methods, and ⑤ how to create instances.

Let's walk through each labeled part. The class name (①) tells Python you're creating a new type called Student. The constructor (②) is the special __init__ method that initializes every new object. Inside it, instance attributes (③) are created using self.attribute_name. The custom methods (④) like enroll and introduce define actions the object can perform. Finally, instantiation (⑤) shows how we create real objects by calling the class name with arguments.

ComponentSyntax ExamplePurpose
Class keywordclass Student:Declares a new class (type)
Constructordef __init__(self, ...):Initializes new instance attributes
Instance attributeself.name = nameStores data unique to each object
Methoddef enroll(self, course):Defines behavior for the object
Instantiationalice = Student("Alice", 10)Creates a new object from the class

Key components of a Python class definition

SECTION 6

Worked Example — Building a BankAccount Class

Let's build a practical class from scratch. We'll create a BankAccount class that tracks an owner's name and balance, and provides methods for deposits and withdrawals.

Building and Using a BankAccount Class

  1. 1

    Step 1 — Define the class and constructor

    Start with the class keyword and write the __init__ method. We need the owner's name, and we'll start the balance at zero by default.
    class BankAccount: def __init__(self, owner): self.owner = owner self.balance = 0.0
  2. 2

    Step 2 — Add a deposit method

    Define a method called deposit that takes an amount and adds it to the balance. We include a check to make sure the amount is positive.
    def deposit(self, amount): if amount > 0: self.balance += amount return self.balance
  3. 3

    Step 3 — Add a withdraw method

    The withdraw method should check that the withdrawal amount does not exceed the current balance. If it does, we print a warning instead of allowing overdraft.
    def withdraw(self, amount): if amount > self.balance: print("Insufficient funds!") else: self.balance -= amount return self.balance
  4. 4

    Step 4 — Create instances and use them

    Now we instantiate two BankAccount objects and perform operations. Each object maintains its own independent balance.
    acct1 = BankAccount("Alice") acct2 = BankAccount("Bob") acct1.deposit(500) # Alice's balance: 500.0 acct2.deposit(1000) # Bob's balance: 1000.0 acct1.withdraw(200) # Alice's balance: 300.0
  5. 5

    Step 5 — Verify independence of instances

    Changing one instance's attributes does not affect the other. After Alice withdraws money, Bob's balance remains untouched. This confirms that each instance holds its own separate data.
    acct1.balance → 300.0, acct2.balance → 1000.0 — each instance is independent.

SECTION 7

Classes vs. Other Approaches

You might wonder why we need classes when we already have dictionaries and plain functions. Both approaches can store and manipulate data, but classes offer significant organizational advantages as programs grow. Let's compare them side by side.

FeatureClassesDictionaries + Functions
Data + behaviorBundled together in one unit — methods live alongside the data they operate onSeparate — functions and dictionaries are not formally linked
Structure enforcementThe constructor guarantees every instance has the same attributesNo enforcement — easy to misspell a key or forget a field
Readabilityacct.deposit(100) reads like natural languagedeposit(acct, 100) is less intuitive
ReusabilityClasses support inheritance, letting you extend and customize existing blueprintsNo built-in mechanism for extending structures
Best forModeling real-world entities with complex behaviorQuick, simple data storage with minimal behavior

Comparing classes with dictionary-based approaches

✦

KEY TAKEAWAY

Using dictionaries and standalone functions for small tasks is perfectly fine — like jotting a quick note on a sticky pad. But when you're building something complex, like a full application, classes are like using a well-organized binder: everything related stays together, is easy to find, and follows a consistent structure. As your projects grow beyond a few hundred lines, classes become essential for keeping your code maintainable.

SECTION 8

Connection to Advanced OOP Concepts

Defining classes and creating instances is the foundation of object-oriented programming, but there is much more to explore. Once you're comfortable with the basics, you'll encounter advanced concepts like inheritance, polymorphism, and encapsulation — all of which build on the class-and-instance model you've just learned.

ConceptWhat You Know NowWhat You'll Learn Next
InheritanceEach class is defined independentlyA child class can inherit attributes and methods from a parent class, reducing code duplication
PolymorphismMethods do one specific thing per classDifferent classes can share method names but implement them differently
EncapsulationAll attributes are openly accessibleYou can hide internal details using naming conventions (underscore prefixes) to protect data
Special methodsYou know __init__Methods like __str__, __repr__, __len__ let your objects work with built-in Python features

Current knowledge vs. upcoming OOP topics

For now, focus on mastering class definitions and instance creation. These concepts form the bedrock on which every advanced OOP technique is built. Once you can confidently define a class, write an __init__ method, add custom methods, and create multiple instances, you'll be ready to tackle inheritance and beyond.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain the difference between a class and an instance in your own words. Why is it useful to have a single class that can produce many instances?
Show answer
A class is a blueprint or template that defines the structure (attributes) and behavior (methods) of a type of object. An instance is a specific object created from that blueprint, holding its own unique data. Having a single class produce many instances is useful because you define the structure once and reuse it, ensuring consistency across all objects while allowing each to hold different values. This avoids duplicating code and makes programs easier to maintain.

Question 2: BASIC

Write a class called Book with an __init__ method that accepts title and author as parameters and stores them as instance attributes. Then create two instances of the class.
Show answer
The class should be defined as: class Book: with def __init__(self, title, author): self.title = title; self.author = author. Then create instances like book1 = Book("1984", "George Orwell") and book2 = Book("To Kill a Mockingbird", "Harper Lee"). Each instance stores its own title and author values independently.

Question 3: INTERMEDIATE

Create a class called Rectangle with attributes width and height. Add two methods: area() that returns the area and perimeter() that returns the perimeter. Create an instance with width 5 and height 3 and call both methods.
Show answer
Define the class with def __init__(self, width, height) storing both as attributes. The area method returns self.width * self.height (which gives 15 for a 5×3 rectangle). The perimeter method returns 2 * (self.width + self.height) (which gives 16). Each method uses self to access the instance's own width and height.

Question 4: APPLIED

Design a Playlist class for a music app. The constructor should take the playlist name and start with an empty list of songs. Include methods add_song(song_title), remove_song(song_title), and total_songs(). Show how two different playlists can have different songs.
Show answer
The constructor initializes self.name and self.songs = []. The add_song method appends to the list, remove_song uses self.songs.remove(song_title) (ideally with an existence check), and total_songs returns len(self.songs). Creating rock = Playlist("Rock Hits") and chill = Playlist("Chill Vibes") demonstrates that adding songs to one playlist doesn't affect the other, because each instance has its own songs list.

Question 5: CRITICAL THINKING

A student writes the following code and finds that changing one instance's data unexpectedly changes another's. Identify the bug and explain the fix: class Team: members = [] def __init__(self, name): self.name = name def add_member(self, person): self.members.append(person) After creating a = Team("Alpha") and b = Team("Beta"), calling a.add_member("Eve") makes "Eve" appear in b.members too. Why?
Show answer
The bug is that members = [] is defined as a class-level attribute rather than an instance attribute. Class attributes are shared across all instances — there is only one members list in memory, and every instance points to it. The fix is to move the list initialization inside __init__: write self.members = [] inside the constructor. This ensures each instance gets its own separate list. This is a common pitfall that highlights the difference between class attributes (shared) and instance attributes (unique per object).

SUMMARY

Summary

Summary

A class is a blueprint that defines attributes (data) and methods (behavior) for a new type of object in Python. You define a class using the class keyword with a PascalCase name. The __init__ constructor runs automatically when you create a new object, and it uses the self parameter to assign data to each individual instance.

An instance is created by calling the class name like a function — a process called instantiation. Each instance holds its own separate data, so changes to one object don't affect another. Classes bundle data and behavior together, making code more organized, readable, and reusable than using loose dictionaries and functions. Mastering class definitions and instance creation is the essential first step in object-oriented programming, paving the way for advanced concepts like inheritance, polymorphism, and encapsulation.

Varsity Tutors • Python • Defining Classes — Define classes and create instances