Know some Python? Write your own interactive tutorial, with runnable examples and exercises, and share it with a link. Write and share your own tutorial.

Start writing

Learn Python / Classes and Objects

Classes and Objects

So far you've stored data in variables, lists and dictionaries, and written functions that work on that data. A class bundles the two together: it describes a kind of thing, the data it holds and the actions it can perform. Each actual thing made from a class is an object.

You've been using objects all along: every string, list and dictionary is an object, and "hello".upper() calls a method on a string object. Now you'll make your own.

Defining a class

A class is a blueprint. By convention, class names use CapitalizedWords:

class Dog:
    pass

rex = Dog()
fido = Dog()

print(rex)
print(type(rex))

# two separate objects
print(rex is fido)
Output

Calling the class like a function, Dog(), creates a new object. That object is also called an instance of the class. Each call makes a different object, which is why rex is fido is False (is asks "are these the very same object?").

Printing an object shows something like <__main__.Dog object at 0x7f...>: the class name and where the object lives in memory. Not very friendly; the Special Methods lesson shows how to choose what gets printed.

The __init__ method

The __init__ method runs automatically whenever a new object is created. It sets up the object's starting data, its attributes:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

rex = Dog("Rex", 3)
fido = Dog("Fido", 7)

print(rex.name, rex.age)
print(fido.name, fido.age)
Output

The arguments you pass to Dog(...) go to __init__. Inside the class, self.name = name stores the value on that particular object, so each dog keeps its own name and age.

Here's what happens when Python runs rex = Dog("Rex", 3):

  1. Python creates a new, empty Dog object.
  2. It calls __init__, passing that new object as self, then "Rex" as name and 3 as age.
  3. self.name = name and self.age = age store the values on the new object.
  4. The finished object is given back and stored in rex.

__init__ has two underscores on each side, and it never returns anything; Python hands back the object for you. In self.name = name, the name on the right is the parameter, and self.name on the left is the attribute stored on the object. Using the same word for both is normal.

Two common mistakes:

  • Forgetting self., as in name = name. That only creates a local variable, which disappears when __init__ ends, so rex.name later gives an AttributeError.
  • Forgetting self in the def line, as in def __init__(name, age):. Python still passes the object first, so you get a TypeError about the number of arguments.

What is self?

self is the object the method is working on. When you write rex.name, you read the name attribute of the rex object. Inside the class, that same object is called self.

Every method's first parameter is self, but you don't pass it yourself: Python fills it in automatically with the object before the dot. In rex.bark(), self is rex; in fido.bark(), self is fido. One method, written once, works for every dog.

self is just a name, but it's such a strong convention that you should always use it.

Methods

A method is a function defined inside a class. Methods can read and change the object's attributes through self:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def bark(self):
        print(f"{self.name} says Woof!")

    def have_birthday(self):
        self.age += 1
        print(f"Happy birthday {self.name}, you're now {self.age}!")

rex = Dog("Rex", 3)
rex.bark()
rex.have_birthday()
print(rex.age)
Output

rex.bark() is really shorthand for Dog.bark(rex). That's how self gets its value.

have_birthday changes self.age, and the change sticks: after the call, rex.age is 4. Methods can also take extra parameters after self, like deposit(self, amount) below, and return values just like ordinary functions.

Changing attributes

Attributes can be read and changed from outside the class too:

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age

rex = Dog("Rex", 3)
rex.age = 4

# a new attribute, on rex only
rex.nickname = "Rexy"

print(rex.age, rex.nickname)
Output

Reading an attribute that doesn't exist, such as rex.colour, is an AttributeError. Attribute names are case-sensitive like everything else, so rex.Name won't find name.

It's usually better to change an object through its methods, which can check that the new value makes sense.

A practical example: a bank account

Classes shine when data and behaviour belong together:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self.balance += amount

    def withdraw(self, amount):
        if amount > self.balance:
            raise ValueError("Insufficient funds")
        self.balance -= amount

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

account = BankAccount("Ada", 100)
account.deposit(50)
account.withdraw(30)
print(account.describe())

try:
    account.withdraw(500)
except ValueError as e:
    print("Error:", e)
Output

The account looks after its own rules: you can't deposit a negative amount or overdraw it.

Going deeper: "Private" attributes with an underscore optional

There's a loophole: nothing stops code outside the class from writing account.balance = -1000 directly and skipping the rules. Python has no truly private attributes. Instead, programmers agree on a convention: a name that starts with an underscore is internal, so don't touch it from outside:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        # internal: use the methods instead
        self._balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def get_balance(self):
        return self._balance

account = BankAccount("Ada", 100)
account.deposit(50)
print(account.get_balance())
Output

Python still lets you read account._balance, but the underscore tells other programmers "you're on your own if you change this". In the Decorators lesson you'll see @property, which lets account.balance be read like an attribute while the class stays in control.

Class attributes

Attributes set on self belong to one object. Attributes defined directly in the class body are shared by all objects of that class:

class Dog:
    # shared by every dog
    species = "Canis familiaris"
    count = 0

    def __init__(self, name):
        # different for each dog
        self.name = name
        Dog.count += 1

a = Dog("Rex")
b = Dog("Fido")

print(a.species, b.species)
print(Dog.count, "dogs created")
Output
Instance attribute Class attribute
Created with self.name = ..., usually in __init__ species = ... directly in the class body
Belongs to one object the class, shared by all its objects
Example each dog's name every dog's species

The counter uses Dog.count += 1, not self.count += 1. Assigning through self would create a separate count attribute on that one dog, and the shared counter would stay at 0.

Objects in collections

Objects work with everything you've learned so far: lists, loops, sorting and more:

class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    def passed(self):
        return self.score >= 50

students = [Student("Mia", 82), Student("Leo", 45), Student("Ava", 91)]

for s in students:
    status = "pass" if s.passed() else "fail"
    print(f"{s.name}: {s.score} ({status})")

best = max(students, key=lambda s: s.score)
print("Top:", best.name)
Output

Exercises

Exercise 1: A Rectangle class

Create a Rectangle class with width and height attributes, and methods area() and perimeter(). The code below should print 12 and 14.

# define Rectangle here

r = Rectangle(3, 4)
print(r.area())
print(r.perimeter())
Output

Exercise 2: A counter

Create a Counter class that starts at 0, with increment(), reset() and a value attribute.

# define Counter here

c = Counter()
c.increment()
c.increment()
c.increment()
print(c.value)    # 3
c.reset()
print(c.value)    # 0
Output

Exercise 3: Shopping cart

Create a Cart class that holds a list of (name, price) items, with add(name, price) and total() methods. Make sure each cart has its own list.

# define Cart here

cart = Cart()
cart.add("Book", 12.5)
cart.add("Pen", 1.25)
print(cart.total())     # 13.75

other = Cart()
print(other.total())    # 0
Output

Exercise 4: Temperature

Create a Temperature class that stores degrees Celsius and has a to_fahrenheit() method. Then add a method is_freezing() that returns True at or below 0°C.

# define Temperature here

t = Temperature(25)
print(t.to_fahrenheit())   # 77.0
print(t.is_freezing())     # False
print(Temperature(-3).is_freezing())   # True
Output

Summary

  • A class is a blueprint, and an object (instance) is a thing made from it: rex = Dog("Rex", 3).
  • __init__ runs when an object is created and sets its attributes on self.
  • self is the object a method is working on. Python passes it automatically.
  • Methods are functions inside a class that use and change the object's data.
  • Attributes on self belong to one object. Class attributes are shared by all objects.

Share this tutorial

Know something worth teaching? Write your own tutorial with runnable examples and share it.