Introduction
When developing object-oriented systems in Python, a common requirement is to encapsulate behavior within classes in a way that balances readability, maintainability, and reusability. A typical scenario might involve designing an Employee class that includes a method to compute an employee’s salary. This method often requires both instance-level data (e.g., hours worked) and class-level constants (e.g., hourly rate). Understanding how to structure this setup correctly helps ensure your code adheres to clean coding principles.
In this article, we’ll explore the most appropriate way to design such a class using instance methods and class constants, and why it is the preferred approach.
Defining the Problem
Imagine you are building an Employee class. Each employee works a different number of hours, but the hourly rate is a fixed value shared across all employees. In such cases:
- The number of hours worked is specific to each employee (instance-level data).
- The hourly rate is the same for all employees (class-level constant).
You want the calculate_salary() method to compute salary using both these pieces of data.
Designing the Class
Here’s how you can structure the Employee class:
class Employee:
HOURLY_RATE = 20 # Class-level constant
def __init__(self, name, hours_worked):
self.name = name
self.hours_worked = hours_worked # Instance variable
def calculate_salary(self):
return self.hours_worked * Employee.HOURLY_RATE

- If the hourly rate might differ across subclasses (e.g., for contractors), you could make it a class variable that’s override-able:
class Employee:
HOURLY_RATE = 20
def __init__(self, name, hours_worked):
self.name = name
self.hours_worked = hours_worked
def calculate_salary(self):
return self.hours_worked * self.__class__.HOURLY_RATE
- Or, make it flexible via a parameter in the constructor if per-employee customization is needed.

Why This Works
- Class Constant (
HOURLY_RATE): Defining the hourly rate as a class variable makes it accessible to all instances of the class. It also allows easy modification at one place, improving maintainability. - Instance Method (
calculate_salary): Since salary depends onhours_worked, which is specific to each instance,calculate_salarymust be an instance method (defwithself). It accesses:self.hours_worked→ instance-level dataEmployee.HOURLY_RATE→ class-level constant
- Encapsulation and Readability: This structure maintains a clear separation of instance-specific data and shared constants, while encapsulating the logic for salary calculation in a single, intuitive method.

Conclusion
When designing methods like calculate_salary in an Employee class, it’s essential to distinguish between what varies per object (instance-level data) and what remains consistent (class-level constants). Using a class constant for the hourly rate and an instance method for the salary calculation is a clean and effective approach. It ensures your code is both modular and easy to maintain, making it easier for others (and your future self) to understand and extend. Properly structuring such patterns is a foundational skill in object-oriented programming.