Python Dataclass vs. Normal Class

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.

Python Dataclass vs. Normal Class

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.

differences between a normal class and a dataclass

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.

Dataclass vs. Normal Class in Python

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.

FeatureNormal ClassDataclass
BoilerplateHigh (Manual __init__, __repr__)Very Low (Auto-generated)
ReadabilityCan be cluttered with logicExtremely clean and data-focused
EqualityManual __eq__ requiredAuto-generated structural equality
Type HintingOptionalRequired for attribute detection
PerformanceStandardSlightly 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:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.