Classes and Objects in Python: A Gentle Introduction

Python for Beginners: Learn to Code with Real Projects · Objects, Libraries and Projects · 6 min read · Updated

Blueprint and objects

  1. A class is a blueprint that describes what data an object has and what it can do.
  2. Calling the class, for example Wallet("Asha", 500), makes an object with its own data.
  3. Another object, even from a related class such as Savings, keeps its own balance.
  4. Methods are the actions an object can do. They can read and change the object data and check it.

So far you have used values that Python provides: numbers, text, lists and dictionaries. A class lets you create your own kind of value that bundles data and the actions that go with it. A bank account has a balance and can deposit and withdraw. A student has marks and can work out a grade. When data and actions belong together, a class keeps them together and makes your code easier to understand. This is called object-oriented programming, and in this lesson you meet it gently.

Class and object

A class is a blueprint. An object (or instance) is something made from that blueprint. If Wallet is the class, then one person's wallet is an object, and another person's wallet is another object. Each object has its own data, called attributes, and its own actions, called methods. Read this program and its output.

from dataclasses import dataclass

class Wallet:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        self.history = []

    def add(self, amount, note=""):
        if amount <= 0:
            raise ValueError("Amount must be positive")
        self.balance += amount
        self.history.append(("add", amount, note))

    def spend(self, amount, note=""):
        if amount > self.balance:
            raise ValueError("Not enough money")
        self.balance -= amount
        self.history.append(("spend", amount, note))

    def __str__(self):
        return f"{self.owner}: balance {self.balance}"

w = Wallet("Asha", 500)
w.add(200, "salary bonus")
w.spend(150, "groceries")
print(w)
print(len(w.history), w.history[-1])

try:
    w.spend(10_000)
except ValueError as error:
    print("Blocked:", error)

@dataclass
class Expense:
    category: str
    amount: float
    paid: bool = False

e = Expense("Food", 250.0)
print(e)
print(e.amount, e == Expense("Food", 250.0))

class Savings(Wallet):
    def add_interest(self, rate):
        self.add(round(self.balance * rate, 2), "interest")

s = Savings("Ravi", 1000)
s.add_interest(0.05)
print(s)
Asha: balance 550
2 ('spend', 150, 'groceries')
Blocked: Not enough money
Expense(category='Food', amount=250.0, paid=False)
250.0 True
Ravi: balance 1050.0

Understanding the class

  • __init__ is the constructor. Python runs it when you write Wallet("Asha", 500). Its first parameter, self, is the object that is being made. self.owner = owner stores the value on that object.
  • Methods are functions inside the class, and they also take self as the first parameter. w.add(200, "salary bonus") is the same as saying: use the add method of the object w. The self is filled in for you.
  • The methods check their input. spend raises a ValueError when you try to spend more than the balance, and the code outside catches it. The class protects its own data from invalid changes.
  • __str__ tells Python how to turn the object into text for print. Without it, you would see an unhelpful text with a memory address.
  • Every object has its own data. Changing one wallet does not change another.

Dataclasses: classes for simple records

Often a class only holds data, like a row of a table. Writing __init__ by hand for that is boring. The @dataclass decorator from the standard library writes it for you from a list of fields with types. It also gives you a readable text form and a == comparison for free, as the output shows: Expense(category='Food', amount=250.0, paid=False), and two expenses with the same values are equal. The = False gives a default value. Dataclasses are a great way to make your data clear.

Inheritance

A class can inherit from another. class Savings(Wallet) means: a Savings wallet is a Wallet with everything Wallet has, plus whatever it adds, here add_interest. We created Savings("Ravi", 1000) and added 5 percent interest, so the balance became 1050.0. Use inheritance when one thing is truly a special kind of another thing. Do not use it only to share a few lines of code. Many experienced programmers prefer to keep the structure simple and use inheritance rarely.

When should I use a class?

  • When you have data and several functions that always work on that same data.
  • When you have many similar things, such as many students or many orders, each with its own state.
  • When a simple dictionary has grown complicated, and you want clear names and checks.
  • You do not need a class for everything. A few plain functions are often the best solution.

Naming and style

  • Class names use CapitalWords: Wallet, Expense. Function and variable names use lowercase_with_underscores.
  • Attributes that are for internal use only start with an underscore, like self._cache. It is a hint to other programmers, and not a lock.
  • Write a short docstring for the class and for each method that is not obvious.

Common mistakes

  • Forgetting self as the first parameter of a method gives a TypeError about the number of arguments.
  • Forgetting self. when you set or read an attribute: balance = 5 inside a method makes only a local variable.
  • Using a mutable default value such as history=[] in __init__. Create the list inside, as the example does with self.history = [].

Your task

  1. Write a Student class with name and a list of marks. Add a method average() and a method grade() that uses the average, with the same rules as lesson 3.
  2. Add __str__ so that print(student) shows the name and the average.
  3. Make a Product dataclass with name, price and stock, and a function that returns the products that are low on stock.
  4. Make Wallet raise an error if someone creates it with a negative starting balance.

How to check it worked

  • A student with marks [80, 90, 70] has average 80.0, and grade B.
  • print(student) shows readable text, not a memory address.
  • Your Wallet("Asha", -5) raises a ValueError.

Frequently asked questions

What is self in Python?

self is the first parameter of a method and refers to the object the method is working on. Python fills it in automatically when you call the method.

When should I use a dataclass?

When a class mostly holds data, like a record or a table row. A dataclass writes the constructor, a readable text form and equality for you.