New: 6 free SQL practice datasets with 300+ questions — try the SQL Compiler →
Python

Python Classes and Objects for Beginners

Python classes and objects for beginners: __init__, self, methods, class vs instance attributes, with real code and output and common mistakes.

Upskly AI Team September 26, 2026 9 min read
Python Classes and Objects for Beginners

A class in Python is a blueprint, and an object (also called an instance) is one thing built from that blueprint. The class describes what data each object holds (its attributes) and what it can do (its methods). You create an object by calling the class like a function: Student("Asha", [90, 85]).

This is the foundation of object-oriented programming. Below is a class built up from scratch, with real output at every step, and the mistakes that catch most beginners.

In this guide

The short version

  • class Name: defines a blueprint. Class names use CapitalisedWords.
  • __init__(self, ...) runs when an object is created and sets up its data.
  • self is the object the method is working on. Every method takes it as the first parameter.
  • Each object keeps its own attributes, so objects from the same class do not interfere.

Why classes exist

Suppose you track students. You could store each one in a dictionary and write separate functions that take that dictionary. It works, but the data and the functions that belong to it are scattered. A class bundles them together: the student’s data and the things a student can do live in one place, and every student you create follows the same design.

Your first class

Here is a small Student class. It stores a name and marks, and can work out an average:

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

    def average(self):
        return sum(self.marks) / len(self.marks)

asha = Student("Asha", [90, 85, 80])
ravi = Student("Ravi", [70, 60])
print(asha.name, asha.average())
print(ravi.name, ravi.average())
print(type(asha).__name__)
print(isinstance(asha, Student))
asha.marks.append(100)
print(len(asha.marks), len(ravi.marks))

Output

Asha 85.0
Ravi 65.0
Student
True
4 2

Let us go through it:

  • class Student: starts the blueprint.
  • __init__ is the constructor. Python calls it automatically whenever you write Student(...). It stores the values on the new object as self.name and self.marks.
  • average is a method: a function that belongs to the class. It reads the object’s own data through self.
  • asha and ravi are two separate objects. The last two lines show they do not share data: adding a mark to Asha did not touch Ravi’s list.

What self really is

self is simply the object the method was called on. When you write asha.average(), Python passes asha in as self automatically. These two calls are the same thing:

class Greeter:
    def hello(self):
        return "hello from " + type(self).__name__

g = Greeter()
print(g.hello())
print(Greeter.hello(g))

Output

hello from Greeter
hello from Greeter

g.hello() is a friendlier way of writing Greeter.hello(g). That is why every method has self as its first parameter, even though you do not pass it yourself. The name self is a convention, but everyone uses it, so you should too.

Instance attributes vs class attributes

An instance attribute (set through self) belongs to one object. A class attribute (set directly in the class body) belongs to the class and is shared by every object. Use class attributes for things like counters and constants:

class Counter:
    total_created = 0

    def __init__(self):
        self.count = 0
        Counter.total_created += 1

    def increment(self):
        self.count += 1

a = Counter()
b = Counter()
a.increment()
a.increment()
b.increment()
print(a.count, b.count)
print(Counter.total_created)

Output

2 1
2

Each counter kept its own count, but total_created lived on the class and was shared, so it reached 2. Objects also allow new attributes to be added later, and __dict__ shows what an object currently holds:

class Box:
    def __init__(self, w):
        self.w = w

b = Box(3)
b.color = "red"
print(b.__dict__)

Output

{'w': 3, 'color': 'red'}

A realistic example: a bank account

Classes are most useful when an object has to protect its own data. This account refuses to overdraw, and the rest of the program can only change the balance through its methods. The leading underscore in _balance is a convention that means “internal, please do not touch directly”:

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

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

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

    def balance(self):
        return self._balance

acc = Account("Asha", 100)
acc.deposit(50)
acc.withdraw(30)
print(acc.balance())
try:
    acc.withdraw(500)
except ValueError as e:
    print("Error:", e)

Output

120
Error: insufficient funds

The rules (positive deposits, no overdrafts) live in one place, so no other part of the program can break them by accident. That idea is called encapsulation. Errors like the ValueError here are covered in Python Exception Handling: try, except, finally.

Making an object print nicely

By default, printing an object shows something unhelpful like its type and a memory address. Define a __str__ method to say how it should appear as text:

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"Point({self.x}, {self.y})"

print(Point(2, 3))
print(f"{Point(4, 5)}")

Output

Point(2, 3)
Point(4, 5)

Methods with double underscores on both sides, like __init__ and __str__, are called dunder (double underscore) or special methods. Python calls them for you at the right moment. We cover more of them in Python Inheritance and Dunder Methods Explained.

Common mistakes

Mistake 1: forgetting self in a method

Python always passes the object as the first argument. If the method does not have a parameter for it, the call fails with a confusing message:

class Counter:
    def increment():
        pass

c = Counter()
c.increment()

Output

TypeError: Counter.increment() takes 0 positional arguments but 1 was given

“Takes 0 positional arguments but 1 was given” is Python’s way of saying you forgot self.

Mistake 2: a mutable class attribute

A list defined in the class body is shared by every object, so one basket’s items appear in all of them. Create it inside __init__ instead:

class Basket:
    items = []

    def add(self, item):
        self.items.append(item)

a = Basket()
b = Basket()
a.add("pen")
print(b.items)

class BasketOk:
    def __init__(self):
        self.items = []

    def add(self, item):
        self.items.append(item)

c = BasketOk()
d = BasketOk()
c.add("pen")
print(d.items)

Output

['pen']
[]

Mistake 3: forgetting self. when using an attribute

Inside a method, write self.name, not just name. A bare name looks for a local variable or a global, not the object’s attribute, and raises a NameError.

Mistake 4: using a class when a function or dictionary would do

Not everything needs a class. If you only have a bit of data, a dictionary is fine. If you only have one action, a function is fine. Reach for a class when data and behaviour belong together, or when you need many similar objects.

Key terms

Class vocabulary
TermMeaningExample
ClassThe blueprintclass Student:
Object / instanceOne thing made from the classasha = Student(…)
AttributeA piece of data on an objectasha.name
MethodA function defined in a classasha.average()
ConstructorThe __init__ method, run on creationdef __init__(self, …)
selfThe object the method is working onself.marks

Try it yourself

Work out each answer first, then open the solution.

1. Write a Rectangle class with width, height and an area() method.

Show solution
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

print(Rectangle(3, 4).area())

Output

12

2. Add an is_square() method that returns True when the sides are equal.

Show solution
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def is_square(self):
        return self.width == self.height

print(Rectangle(2, 2).is_square(), Rectangle(2, 3).is_square())

Output

True False

3. What does this print?

class A:
    x = 1

a = A()
a.x = 5
print(A.x, a.x)
Show answer

Output

1 5

a.x = 5 created a new instance attribute on a, which hides the class attribute for that object. The class attribute A.x is still 1.

4. What does this print?

class Counter:
    def __init__(self):
        self.count = 0

    def increment(self):
        self.count += 1

c1 = Counter()
c2 = Counter()
c1.increment()
print(c1.count, c2.count)
Show answer

Output

1 0

Each object has its own count. Incrementing c1 does not affect c2.

Try these in our free Python compiler.

Frequently asked questions

What is the difference between a class and an object in Python?

A class is the blueprint that defines attributes and methods. An object (instance) is a specific thing created from that class, with its own values. One class can produce many objects.

What is self in Python?

self is the first parameter of a method, and it refers to the object the method is being called on. Python passes it automatically, so obj.method() works like Class.method(obj).

What does __init__ do?

It is the constructor: Python calls it automatically when a new object is created, so you can set up the object’s starting attributes. It should not return a value.

What is the difference between a class attribute and an instance attribute?

A class attribute is defined in the class body and shared by all objects. An instance attribute is set on self and belongs to one object only.

What is the difference between a function and a method?

A method is a function defined inside a class and called on an object. It receives that object as self. A plain function is not attached to any class.

Do I always need classes in Python?

No. Python works well with plain functions and built-in types. Use classes when data and the actions on it belong together, or when you need many similar objects with their own state.

Run this code in your browser

The free Upskly compiler runs Python with nothing to install. Paste the example, change it and see what happens.

Open Python Compiler

Stuck on a traceback?

AI Assist works inside the Python notebook, so you can ask about an error or a concept without leaving the cell.

Try AI Assist

Test yourself

Timed questions on Classes and Object-Oriented Python, with an explanation for every answer.

Take the Quiz
Upskly AI Team
Learning made simple
Scroll to Top