Python Normal Classes vs. Data Classes: Which Should You Use?

Romeo Agbor Peter - Mar 31 '23 - - Dev Community

Hey 👋🏿, it's been a while I put out a technical piece on anything. I was jaded from things happening around me but now I'm back and I've got loads of topic and concept in tech I want to share. For starters, while researching on Python's class data, I decided to write a technical piece about it. Hope you fine it useful.

Python is an object-oriented programming language that provides different ways to define and use classes. Two popular options are data classes and normal classes. In this article, we'll explore the differences between these two types of classes and help you decide which one to use in your Python code.

What are Normal Classes?

In Python, a class is a blueprint for creating objects. A normal class is a user-defined class that doesn't have any special attributes or methods. It has a constructor method __init__ that initializes the class instance with default or user-provided values. A normal class can also have additional methods defined within its scope.

Here is an example of a normal class in Python:

class Dog:
    def __init__(self, name, breed, age):
        self.name = name
        self.breed = breed
        self.age = age
Enter fullscreen mode Exit fullscreen mode

What are Data Classes?

A data class is a special type of class that is designed to store data. In Python, data classes are created using the @dataclass decorator. They are meant to be used for simple, immutable data structures that have no behavior.

Here is an example of a data class in Python:

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
Enter fullscreen mode Exit fullscreen mode

In the code above, The Person data class using the @dataclass decorator. We also defined two fields name and age which are annotated with their respective types.

differences between Normal and Data classes

1- Conciseness: Data classes are more concise than normal classes as they require less code to define. The fields are automatically generated, and a __repr__ method is provided for us.

Here is an example that compares the conciseness of a normal class and a data class:

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

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

@dataclass
class Point2:
    x: int
    y: int
Enter fullscreen mode Exit fullscreen mode

In the above example, the Point class has a constructor method and a __repr__ method, while the Point2 data class only has the two fields x and y. The __repr__ method is automatically generated for Point2.

2- Boilerplate code: One of the biggest differences between normal classes and data classes is the amount of boilerplate code needed to define them.

When defining a normal class, we need to define the __init__ method, as well as getters and setters for each attribute, if we want to be able to access and modify them from outside the class. This can quickly become cumbersome when dealing with classes with many attributes.

Here's an example of a normal class that defines two attributes, name and age:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def get_name(self):
        return self.name

    def set_name(self, name):
        self.name = name

    def get_age(self):
        return self.age

    def set_age(self, age):
        self.age = age
Enter fullscreen mode Exit fullscreen mode

On the other hand, when defining a data class, we only need to define the attributes as class variables, and the boilerplate code is generated for us automatically. This makes data classes much more concise and easier to read.

Here's an example of a data class that defines the same two attributes, name and age:

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int
Enter fullscreen mode Exit fullscreen mode

3- Immutability: Another key difference between normal classes and data classes is immutability. By default, data classes are immutable, meaning that their attributes cannot be modified after they are created.

This is achieved by adding the frozen=True argument to the dataclass decorator:

from dataclasses import dataclass

@dataclass(frozen=True)
class Person:
    name: str
    age: int
Enter fullscreen mode Exit fullscreen mode

In contrast, normal classes are mutable by default, meaning that their attributes can be modified at any time. While mutability can be useful in certain cases, it can also make it difficult to reason about the state of an object, particularly in multi-threaded environments.

4- Type Hinting: Type hinting is another area where data classes and normal classes differ. With data classes, we can specify the types of our attributes in the class definition. This is done using Python's built-in type annotations. Here's an example:

from typing import List

@dataclass
class Person:
    name: str
    age: int
    hobbies: List[str]
Enter fullscreen mode Exit fullscreen mode

As you can see, we specified the types of the name, age, and hobbies attributes. This allows Python's type checker to catch any type errors during development, which can help prevent bugs and improve the reliability of our code.

In contrast, with normal classes, we have to manually specify the types of our attributes in the constructor or in the class definition. Here's an example:

from typing import List

class Person:
    def __init__(self, name: str, age: int, hobbies: List[str]):
        self.name = name
        self.age = age
        self.hobbies = hobbies
Enter fullscreen mode Exit fullscreen mode

4 Equality: By default, data classes define equality based on the values of their attributes, meaning that two instances of a data class are considered equal if all their attributes have the same values.

from dataclasses import dataclass

@dataclass
class Person:
    name: str
    age: int

p1 = Person("Alice", 30)
p2 = Person("Alice", 30)
print(p1 == p2)  # True
Enter fullscreen mode Exit fullscreen mode

In contrast, normal classes define equality based on object identity, meaning that two instances of a normal class are considered equal only if they are the same object in memory.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

p1 = Person("Alice", 30)
p2 = Person("Alice", 30)
print(p1 == p2)  # False
Enter fullscreen mode Exit fullscreen mode

5 Overriding default methods: Data classes provide default implementations for several methods such as __init__(), __repr__(), __eq__(), __ne__(), __hash__() etc. These default implementations are generated based on the attributes defined in the class. This allows us to avoid writing boilerplate code and focus on the logic of our program.

On the other hand, with normal classes, we have to define these methods ourselves if we want to use them. Here's an example of a normal class that implements these methods:

class Person:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"Person(name='{self.name}', age={self.age})"

    def __eq__(self, other):
        if isinstance(other, Person):
            return self.name == other.name and self.age == other.age
        return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash((self.name, self.age))
Enter fullscreen mode Exit fullscreen mode

Conclusion

Data classes and normal classes are both used in Python to represent objects. Data classes are a more concise and readable way of defining classes that are primarily used to store data, whereas normal classes are more flexible and customizable.

Data classes are preferred when we have simple data structures and we want to avoid writing boilerplate code. They also provide several default methods such as __init__(), __repr__(), __eq__(), __ne__(), __hash__() that we don't have to write ourselves.

Normal classes, on the other hand, are more flexible and customizable. They allow us to define our own methods and attributes and provide us with complete control over our classes.

In summary, data classes are great for simple data structures and normal classes are more appropriate when we need more flexibility and control over our classes.

PS: Did you find this useful? If so then follow me for more article updates and tweets ✨

. . . . . . . . . . . . . . . . . . .