Project: Class-Based Data Handler for Python Beginners
Learn how to refactor procedural data logic into a clean, object-oriented structure using inheritance to handle multiple file types with ease.

Previously in this course, we covered Inheritance in Python and Methods and Attributes. In this lesson, we are going to take the procedural data-processing logic we built in Project: Building an API-Integrated Data Tool in Python and refactor it into a structured, class-based system.
By moving from loose functions to a class-based architecture, you’ll stop scattering file-handling logic across your project. This approach—similar to how we handled Modularizing the Cache Service: A Practical Guide to OOP in Redis—makes your code easier to test, extend, and debug.
Defining the DataProcessor Class
When building a data-processing tool, you often find yourself repeating the same patterns: opening a file, reading its content, and parsing it. If you add a new file format, you end up writing a new, almost identical function.
A DataProcessor base class allows us to define the "interface" for all data operations. Even if the internal logic for reading a CSV differs from reading JSON, the method names remain consistent.
The Base Class Structure
We start with a base class that defines what any "Processor" should be able to do. We use a base class to enforce consistency:
PYTHONimport json import csv class DataProcessor: def __init__(self, file_path: str): self.file_path = file_path def load_data(self): CE9178">"""Standard interface for loading data.""" raise NotImplementedError("Subclasses must implement load_data()") def save_data(self, data): CE9178">"""Standard interface for saving data.""" raise NotImplementedError("Subclasses must implement save_data()")
By raising NotImplementedError, we ensure that any developer (including "future you") knows they must define the specific logic when creating a child class.
Implementing Inheritance for Specific File Types

Now, let’s create specialized classes for our JSON and CSV files. These classes will "inherit" the file_path storage from the parent but provide their own implementation for reading and writing.
PYTHONclass JSONProcessor(DataProcessor): def load_data(self): with open(self.file_path, CE9178">'r') as f: return json.load(f) def save_data(self, data): with open(self.file_path, CE9178">'w') as f: json.dump(data, f, indent=4) class CSVProcessor(DataProcessor): def load_data(self): with open(self.file_path, CE9178">'r') as f: return list(csv.DictReader(f)) def save_data(self, data): if not data: return keys = data[0].keys() with open(self.file_path, CE9178">'w', newline=CE9178">'') as f: writer = csv.DictWriter(f, fieldnames=keys) writer.writeheader() writer.writerows(data)
Improving Modularity
The beauty of this OOP (Object-Oriented Programming) structure is that your main application no longer cares how the data is processed. It only cares that it has a load_data method.
If you decide to add an XML parser later, you simply create an XMLProcessor class. You don't have to touch your existing JSONProcessor or your main loop logic. This is the definition of refactoring for maintainability.
Hands-on Exercise
- Take your existing
DataCollectorproject code. - Replace your procedural
load_jsonandsave_jsonfunctions with theJSONProcessorclass shown above. - Update your main script to instantiate the class:
processor = JSONProcessor("data.json")and callprocessor.load_data().
Common Pitfalls
- Forgetting
self: In methods, ensure every reference to the class attributes usesself.file_path. - Hardcoding file logic: Avoid putting file-specific logic (like
json.load) inside the base class. Keep the base class abstract. - Over-engineering: Don't create a class for every single tiny utility function. Use classes when you have shared state or shared behavior across multiple types of data.
FAQ
- Why use a base class instead of just functions? Classes allow you to bundle the file path (state) with the behavior (reading/writing), making it easier to pass the "processor" around your app.
- Can I have multiple inheritance? Yes, but for data handlers, it is rarely needed. Stick to single inheritance where a class specializes in one data format.
- Is this faster than procedural code? Performance is identical; the benefit is entirely in developer productivity and project organization.
Recap
We successfully refactored our procedural data logic into a cohesive DataProcessor hierarchy. By using a base class to define our interface and specialized subclasses for specific file formats, we’ve created a modular system that is ready to grow with our project.
Up next: Packaging Python Projects — we will take this code and structure it as a proper, installable module.
Work with me

AI Automation & Agentic Workflow Development
Automate the repetitive work eating your time — content pipelines, data workflows, and agentic AI tasks that run themselves.

React & Next.js Dashboard / Admin UI Development
A clean, data-rich dashboard UI in React or Next.js — charts, tables, and real-time data that your users will actually enjoy using.


