A Python class is a blueprint for creating objects. Once you understand how classes work, a large portion of Python's standard library and every third-party package you install will start making sense. This tutorial walks through everything a beginner needs: what a class is, how to write one, what __init__ and self actually do, and how to create and use instances.
Before Python had classes, programs were written as sequences of functions passing data around. That works for small scripts, but as programs grow it becomes hard to keep related data and behavior together. Classes solve that problem by letting you group both in one place.
What Is a Class?
Think of a class as a cookie cutter and instances as the cookies. The cookie cutter defines the shape, but it is not a cookie itself. Every time you use it, you get a new cookie with the same shape but potentially different toppings. In Python terms, the class defines the structure and behavior, and each instance is a separate object that follows that structure while holding its own data.
The Python documentation describes a class as providing a mechanism for bundling data and functionality together. A new class creates a new type of object, allowing new instances of that type to be made. The simplest possible class you can write looks like this:
class Dog:
pass
That is a valid class. The pass keyword tells Python to do nothing — it is a placeholder when the body would otherwise be empty. This class has no attributes and no methods, but Python has registered it as a type you can create objects from.
Python class names use PascalCase by convention: every word is capitalized with no underscores. Dog, BankAccount, and HttpClient are all valid class names. Functions and variables use snake_case by contrast.
What Is an Instance?
An instance is a specific object created from a class. You create one by calling the class like a function:
class Dog:
pass
rex = Dog()
buddy = Dog()
print(type(rex)) # <class '__main__.Dog'>
print(rex is buddy) # False — two separate objects
rex and buddy are both instances of Dog. They are different objects in memory, meaning changes to one do not affect the other. The type() call confirms that rex is a Dog.
Build the correct statement to define a Python class named Cat:
class keyword (not def, which is for functions). The class name follows in PascalCase — so Cat not cat. A colon ends the line, just like if statements and function definitions. The = sign is not used here.
Anatomy of a Python Class
A real class does more than just exist — it holds data and behavior. The three elements you will see in almost every class are the __init__ method, instance attributes assigned through self, and additional methods that define what the object can do.
class Dog:
"""A simple class representing a dog."""
def __init__(self, name, age):
self.name = name # instance attribute
self.age = age # instance attribute
def bark(self):
print(f"{self.name} says: woof!")
def describe(self):
print(f"{self.name} is {self.age} year(s) old.")
rex = Dog("Rex", 4)
rex.bark() # Rex says: woof!
rex.describe() # Rex is 4 year(s) old.
What __init__ Does
__init__ is the initializer method. Python calls it automatically the moment you create a new instance. It is not a constructor in the strict sense (Python uses __new__ for that), but for beginners it acts like one: it sets up the instance's starting state. The double underscores on both sides of the name indicate it is a special method that Python knows about and calls at a specific moment.
Methods whose names start and end with double underscores are called dunder methods (short for double underscore). Python defines many of them: __str__, __repr__, __len__, and others. You will learn them gradually as you write more classes.
What self Does
Every method in a class receives the current instance as its first argument. By convention that argument is named self. When you write rex.bark(), Python translates that call internally to Dog.bark(rex) — it passes rex as self. Inside bark, self.name therefore refers to rex.name.
You must include self as the first parameter in every instance method signature, but you never pass it explicitly when calling the method. Python handles that automatically.
"Classes provide a means of bundling data and functionality together." — Python Software Foundation, Python 3 Tutorial
The class below has one bug. Click the line you think is wrong, then hit check.
self.make = make. Without self., the assignment creates a local variable named make that disappears when __init__ returns. The instance never gains a make attribute, so line 6's self.make raises an AttributeError.
Class Attributes vs Instance Attributes
Python distinguishes between two kinds of attributes. Understanding the difference prevents a common category of beginner bugs.
An instance attribute is attached to a specific instance. You create it inside a method by assigning to self.something. Each instance has its own copy.
A class attribute is defined directly in the class body, outside any method. It is shared across all instances unless an instance overrides it with its own value of the same name.
class Dog:
species = "Canis lupus familiaris" # class attribute — shared
def __init__(self, name):
self.name = name # instance attribute — unique per object
rex = Dog("Rex")
buddy = Dog("Buddy")
print(rex.species) # Canis lupus familiaris
print(buddy.species) # Canis lupus familiaris — same value
print(rex.name) # Rex
print(buddy.name) # Buddy — different values
Modifying a class attribute through the class itself affects all instances that have not overridden it. Modifying it through an instance creates a new instance attribute that shadows the class attribute for that instance only.
Using a mutable object (a list or dictionary) as a class attribute is a common bug source. All instances share the same list, so appending to it from one instance affects all others. Use instance attributes (assigned in __init__) for any mutable per-instance data.
- Where defined
- Inside a method, using
self.attr = value - Scope
- Unique to each instance — changing it on one object does not affect others
- Where defined
- Directly in the class body, outside any method
- Scope
- Shared across all instances unless shadowed by an instance attribute of the same name
- Where defined
- Inside the class body using
def method_name(self, ...): - Scope
- Available on all instances; called with dot notation, e.g.
my_obj.method()
How to Write a Python Class
Follow these steps every time you need to define a new class from scratch. The pattern applies whether you are modeling a dog, a user account, or a network packet.
-
Declare the class
Use the
classkeyword followed by a PascalCase name and a colon. Add a docstring on the next indented line to describe what the class represents. Example:class BankAccount: -
Define __init__
Inside the class, write
def __init__(self, ...):with whatever parameters the object needs at creation time. Assign each one toself.attribute_nameso it is saved on the instance. Example:self.balance = initial_balance -
Add methods
Define additional methods inside the class body. Each must take
selfas its first parameter. Useself.attributeto read or update instance data. Example:def deposit(self, amount): self.balance += amount -
Create instances
Outside the class definition, call the class like a function and assign the result to a variable. Pass any arguments that
__init__expects (exceptself). Example:account = BankAccount(500) -
Access attributes and call methods
Use dot notation to read attributes and call methods on the instance. Example:
print(account.balance)reads the attribute;account.deposit(100)calls the method and updates the balance.
Putting it all together, a complete BankAccount class looks like this:
class BankAccount:
"""Represents a simple bank account."""
def __init__(self, owner, initial_balance=0):
self.owner = owner
self.balance = initial_balance
def deposit(self, amount):
self.balance += amount
print(f"Deposited {amount}. New balance: {self.balance}")
def withdraw(self, amount):
if amount > self.balance:
print("Insufficient funds.")
else:
self.balance -= amount
print(f"Withdrew {amount}. New balance: {self.balance}")
def show_balance(self):
print(f"{self.owner}'s balance: {self.balance}")
account = BankAccount("Alice", 200)
account.deposit(50) # Deposited 50. New balance: 250
account.withdraw(300) # Insufficient funds.
account.show_balance() # Alice's balance: 250
Python Classes: Key Learning Points
- A class is defined with the
classkeyword and a PascalCase name. It is a blueprint, not an object itself. __init__runs automatically when you create an instance and is where you set up instance attributes usingself.selfis always the first parameter of an instance method and refers to the calling instance. You never pass it manually — Python does.- Instance attributes (set with
self.attr) belong to one object. Class attributes (set in the class body) are shared by all instances. - You access attributes and call methods using dot notation:
obj.attributeandobj.method().
Classes are the foundation of object-oriented programming in Python. Once you are comfortable with what is covered here — defining a class, writing __init__, using self, and creating instances — you are ready to move on to inheritance, where one class can build on another.
Frequently Asked Questions
A class in Python is a blueprint for creating objects. It defines a set of attributes (data) and methods (functions) that every object created from that class will have. You define a class using the class keyword followed by the class name and a colon.
A class is the template or blueprint, while an instance is a specific object created from that template. You can create many instances from one class, and each instance holds its own data while sharing the class's methods.
__init__ is the initializer method that Python calls automatically when you create a new instance of a class. It sets up the instance's starting data (attributes). The double underscores indicate it is a special method built into Python's object system.
self is a reference to the current instance of the class. When you call a method on an object, Python automatically passes that object as the first argument. By convention, this parameter is always named self, though technically any name works.
A class attribute is defined directly on the class and is shared by all instances. An instance attribute is defined inside a method (typically __init__) using self, and each instance has its own separate copy of it.
No. __init__ is optional. If you do not define it, Python uses a default one that does nothing. You only need __init__ when your instances need starting data set up at creation time.
You create an object by calling the class like a function. For example, if your class is named Dog, you write my_dog = Dog('Rex', 3) to create a new instance. Python automatically calls __init__ with the arguments you provide.
A method is a function defined inside a class. Methods always take self as their first parameter so they can access and modify the instance's data. You call a method on an instance using dot notation, for example my_dog.bark().
Yes. A class can have as many methods as needed. Each method is a function defined inside the class body, and each one receives self as its first parameter to access the instance.
Dot notation is how you access attributes and call methods on an object. The dot connects the object name to the attribute or method name. For example, my_dog.name accesses the name attribute, and my_dog.bark() calls the bark method.