Learn by reading through in order

Classes and Instances — Defining Your Own Type

Learn Python classes and instances from the ground up. Walk through class definitions, the role of self, and using __init__ to set attributes — all with diagrams.

As we touched on in the previous wrap-up, built-in types like int, str, list, and dict can't directly represent business concepts like "user," "product," or "order." The next step up is to define your own type with class — that's the entry point into object-oriented programming (OOP).

Why Object-Oriented Programming?

Everything you've written so far is procedural — combining functions to drive behavior. As programs grow, related data and the logic that operates on it tend to scatter, and changes and reuse get harder.

With object-oriented programming (OOP), you bundle the related data (attributes) and the operations on it (methods) into a single unit — an object. That lets you shape your code around real-world concepts.

Procedural vs OOP — How Data and Functions Relate
ProceduralData(variables)Functions(elsewhere)OOPObjectdata + methodsBundledtogetherseparateresult
In procedural code, data and functions live in completely separate places, and you pass the data into functions every time as function(data). OOP puts both inside an object so they live together.

A Class Is a Blueprint, an Instance Is the Real Thing

Two terms anchor everything in OOP:

- Class — a blueprint that lists the attributes and methods you'll have

- Instance — the real thing built from that blueprint

For example, define a Product class to capture the concept of a product, and you can spin up as many individual product instances — apple, banana, orange — as you need. Together, classes and instances are called objects.

Building Multiple Instances from One Class
Product(class)applename='apple'banananame='banana'orangename='orange'createcreatecreate
A class is like a stencil — you don't use it directly. The instances born from it actually hold data and have methods called on them.

Defining a Minimal Class

Let's actually define a Product class for items in a store. The syntax is class ClassName:. By convention, Python class names use CapitalizedCamelCase (Product, UserAccount, etc.).

A variable written directly inside the class — like name = "apple" — is treated as a default value held by the class, and you can read it via the class name as Product.name.

class Product:
    name = "apple"
    price = 150

# Reach into the class directly
print(Product.name)    # apple
print(Product.price)   # 150
Variables That Stick to the Class — Class Variables
Product(class body)name='apple'price=150holdsholds
Variables you write right under classname / pricestick to the class itself and can be read directly via the class name as Product.name.

Variables written directly under class are called class variables. They behave differently from instance variables (which each instance owns separately), but we'll come back to that distinction in a later article. For now, all you need is the minimal idea: "stick a value to the class itself, then read it as Product.name."

Build a tiny class for inventory.

① Define class Product:.

② Inside, add three values: name = "apple", price = 150, and stock = 10.

③ Run print(Product.name) / print(Product.price) / print(Product.stock) and confirm you can read each value directly from the class name.

(If you run it correctly, an explanation will appear.)

Python Editor

Run code to see output

Use __init__ to Give Each Instance Its Own Values

The minimal class above just stuck a fixed value like name = "apple" to the class, so every instance you build would be "apple." In real code, you want multiple instances from one class — apple, banana, orange — each with its own values.

The tool for this is Python's special method __init__ — also known as the constructor or initializer. When you call Product("apple", 150) with arguments, Python automatically calls __init__, and inside it you write self.name = name to store values on the instance itself.

Double Underscores — A Python Convention

Names wrapped in double underscores like __init__ are called dunder methods. Python gives them special meaning and calls them automatically at certain moments__init__ when an instance is created, __str__ when something is printed, and so on. We'll cover __init__ in more depth and meet __del__ in the next article.

What Happens When You Call Product("apple", 150)
apple = Product( 'apple', 150)__init__(self, name, price)self.name = nameself.price = priceapplename='apple', price=150auto-callassignready
Calling Product("apple", 150) makes Python ① prepare an empty instance, ② pass it to __init__ as self, and ③ write the arguments onto that instance's attributes — leaving you with a finished object.
class Product:
    def __init__(self, name, price):    # Called automatically when an instance is created
        self.name = name
        self.price = price

apple = Product("apple", 150)
banana = Product("banana", 80)

print(apple.name, apple.price)     # apple 150
print(banana.name, banana.price)   # banana 80

# apple and banana are different objects
print(apple is banana)             # False
Many Instances from One Class
Product(class)applename='apple', price=150banananame='banana', price=80orangename='orange', price=120
Every call to Product(...) creates a separate object. apple, banana, and orange all come from the same blueprint but are different objects, each with its own name / price.

Write a Product class with __init__ and create multiple instances.

① Define class Product:. Inside def __init__(self, name, price):, write self.name = name and self.price = price.

② Create two instances: apple = Product("apple", 150) and banana = Product("banana", 80).

③ Run print(apple.name, apple.price) and print(banana.name, banana.price) and confirm each carries its own values.

(If you run it correctly, an explanation will appear.)

Python Editor

Run code to see output

How Methods and self Work

Attributes alone just "hold data" — not much different from a plain dict. The real point of OOP is that you can also write operations on the instance alongside the data. Those operations are called methods.

The Rules for Methods and self
class Product:def show(self): ...1st parameterself (required)the callinginstance is passedautomaticallyself.name→ attribute accessinsidedeclarevaluevia
A function written inside a class with def is a method. The first parameter self is required, and on each call Python fills it with the instance you called the method on. Inside the method, write self.name to access that instance's attributes.

When you write apple.show(), Python is internally running Product.show(apple)self ends up holding apple.

self Always Points to the Caller
Module
  • apple and banana are separate instances
  • When you call apple.show()
show(self) frame
  • self is filled with apple automatically
  • self.name reads apple.name
  • For banana.show(), self becomes banana instead
self isn't a fixed value — it's whichever instance you called the method on. With the same show method, calling from apple reads apple's name, calling from banana reads banana's.
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def show(self):                         # First parameter is always self
        print(f"{self.name}: ${self.price}")

apple = Product("apple", 150)
banana = Product("banana", 80)

apple.show()    # apple: $150  same as Product.show(apple) internally
banana.show()   # banana: $80   same as Product.show(banana) internally
self Is Decided When the Method Is Called
apple.show()self = appleapple: $150banana.show()self = bananabanana: $80passoutputpassoutput
Calling apple.show() puts apple into self, while calling banana.show() puts banana into self. The same show method runs with a different self every time, depending on the caller.

Add one method to Product and call it through an instance.

① Define class Product: with __init__(self, name, price) setting self.name / self.price.

② Inside the class, define def show(self): that prints f"{self.name}: ${self.price}".

③ Build apple = Product("apple", 150) and banana = Product("banana", 80), then call apple.show() and banana.show() to confirm the same method runs with each instance's values.

Python Editor

Run code to see output

self Is Just a Convention

Syntactically, the first parameter can be anything — def show(this): works too. But almost every Python codebase uses self, so stick with it. Anything else throws off readers.

In this article you got the basics of object-oriented programming. We'll cover variables and methods in more depth in later articles.

QUIZ

Knowledge Check

Answer each question one by one.

Q1Which of the following is the correct way to define a Python class?

Q2What is automatically passed to a method's first parameter self when called?

Q3What does the following code print?
class P:
def __init__(self, x):
self.x = x
a = P(10)
b = P(20)
print(a.x + b.x)