I remember the first time I had to build a large-scale inventory system for a retail client. I spent hours writing repetitive __init__ methods and manual equality checks for hundreds of product types.
It was tedious and felt like I was fighting the language rather than using it to my advantage.
Then I discovered Python Dataclasses, and it completely changed how I structure my data-heavy projects.
In this tutorial, I will show you the real differences between a normal class and a dataclass so you can decide which fits your workflow.
What is a Normal Class in Python?
A normal class is the traditional way we define objects in Python using the class keyword.
When I use a normal class, I have to manually define how the object is initialized and how it should be represented.
Here is an example of a normal class representing a Tech Employee in a Silicon Valley firm:
class Employee:
def __init__(self, name: str, role: str, salary: int, city: str):
self.name = name
self.role = role
self.salary = salary
self.city = city
def __repr__(self):
return f"Employee(name={self.name}, role={self.role}, salary={self.salary}, city={self.city})"
def __eq__(self, other):
if not isinstance(other, Employee):
return NotImplemented
return (self.name, self.role, self.salary, self.city) == \
(other.name, other.role, other.salary, other.city)
# Creating instances
emp1 = Employee("Alice", "DevOps Engineer", 140000, "San Francisco")
emp2 = Employee("Alice", "DevOps Engineer", 140000, "San Francisco")
print(emp1)
print(f"Are they equal? {emp1 == emp2}")You can see the output in the screenshot below.

In the example above, I had to write the name of each attribute multiple times just to get basic functionality.
If I didn’t write the __repr__ method, printing the object would just show a messy memory address.
What is a Dataclass in Python?
A dataclass is a regular class that is decorated with @dataclass from the dataclasses module.
It was introduced in Python 3.7 to specifically help developers like us who deal with classes that primarily store data.
The decorator automatically writes the __init__, __repr__, and __eq__ methods for you behind the scenes.
Let’s rewrite that same Tech Employee example using a dataclass:
from dataclasses import dataclass
@dataclass
class Employee:
name: str
role: str
salary: int
city: str
# Creating instances
emp1 = Employee("Alice", "DevOps Engineer", 140000, "San Francisco")
emp2 = Employee("Alice", "DevOps Engineer", 140000, "San Francisco")
print(emp1)
print(f"Are they equal? {emp1 == emp2}")You can see the output in the screenshot below.

The difference is night and day; I went from 15 lines of boilerplate to just 6 lines of clean, readable code.
Use Default Values and Type Hinting
One thing I love about dataclasses is how easily they handle default values.
In a normal class, you’d have to manage these in the __init__ arguments, which can get messy.
In a dataclass, you can assign them directly next to the type hint.
Suppose we are tracking Real Estate listings in New York City where most listings are “Active” by default.
from dataclasses import dataclass
@dataclass
class NYCListing:
address: str
price: int
sq_ft: int
status: str = "Active" # Default value
listing = NYCListing("5th Ave, NY", 2500000, 1200)
print(listing)You can see the output in the screenshot below.

This makes the code much more intuitive for anyone reading your scripts for the first time.
Implement Immutability with Frozen Dataclasses
In some of my high-security financial projects, I need to ensure that data cannot be changed once it’s created.
With a normal class, you’d have to jump through hoops with @property and private variables.
With a dataclass, I just set frozen=True in the decorator.
from dataclasses import dataclass
@dataclass(frozen=True)
class CreditCardTransaction:
transaction_id: str
amount: float
vendor: str
tx = CreditCardTransaction("TXN1001", 45.50, "Starbucks")
# This will raise an error
# tx.amount = 50.0 This is a lifesaver when you want to treat your objects as “read-only” constants across your application.
Handle Post-Initialization Logic
Sometimes, you need to calculate a field based on other inputs immediately after the object is built.
Dataclasses provide a special hook called __post_init__ for exactly this purpose.
In this example, let’s calculate the “Tax Amount” for a purchase made in Texas.
from dataclasses import dataclass, field
@dataclass
class TexasOrder:
item_name: str
price: float
tax_rate: float = 0.0625 # 6.25% state tax
total_with_tax: float = field(init=False)
def __post_init__(self):
self.total_with_tax = self.price + (self.price * self.tax_rate)
order = TexasOrder("Cowboy Boots", 200.00)
print(f"Total price in Texas: ${order.total_with_tax:.2f}")I used field(init=False) because I don’t want the user to pass the total price manually; I want the code to handle it.
Compare Normal Class and Dataclass
To make it easier for you to decide, I have summarized the key differences based on my experience.
| Feature | Normal Class | Dataclass |
| Boilerplate | High (Manual __init__, __repr__) | Very Low (Auto-generated) |
| Readability | Can be cluttered with logic | Extremely clean and data-focused |
| Equality | Manual __eq__ required | Auto-generated structural equality |
| Type Hinting | Optional | Required for attribute detection |
| Performance | Standard | Slightly faster for creation and access |
I usually stick to normal classes when I have complex behaviors or methods that don’t just “store” data.
However, for API responses, database models, or configuration settings, dataclasses are my go-to choice.
I hope you found this tutorial on the difference between normal classes and dataclasses in Python useful!
Whether you are building a small automation script or a large enterprise application, choosing the right class type can save you a lot of debugging time.
You may read:
- Python Composition vs Inheritance
- Python Aggregation vs Composition
- How to Build a Simple OOP Project in Python
- How to Use @dataclass in Python

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.