Using Class Methods, Static Methods, and Decorators in Python
Course: Python Training
Topic: Using Class Methods, Static Methods, and Decorators
Course Link: Python Training Course at JustAcademy
Demo Link: Register for a Free Course Demo
Website: JustAcademy
1. Introduction
Python provides different types of methods to organize code inside classes. Instance methods, class methods, and static methods serve different purposes depending on whether a method needs access to an individual object, the class itself, or neither.
Python also supports decorators, which allow developers to modify or extend the behavior of functions and methods without changing their original implementation. Understanding these concepts is essential for writing reusable, maintainable, and well-structured object-oriented Python applications.
Class methods, static methods, and decorators are commonly used in applications involving object creation, configuration management, validation, logging, utility operations, and reusable business logic.
2. Learning Objectives
- Understand the purpose of instance methods, class methods, and static methods.
- Learn how the
@classmethod decorator works.
- Understand the role of the
@staticmethod decorator.
- Learn how decorators modify function behavior.
- Understand the difference between
self and cls.
- Explore method decorators, custom decorators, and decorator chaining.
- Apply these concepts through practical Python examples.
3. Understanding Methods in Python
A method is a function defined inside a class. Methods help describe the behavior of objects and organize related operations into a logical structure.
Python commonly uses three method types:
| Method Type |
First Parameter |
Access to Instance |
Access to Class |
Typical Purpose |
| Instance method |
self |
Yes |
Through the instance |
Working with individual object data |
| Class method |
cls |
No automatic instance reference |
Yes |
Working with class-level data or creating objects |
| Static method |
No automatic first parameter |
No |
No automatic class reference |
Utility operations related to a class |
4. Understanding Instance Methods
An instance method operates on a particular object. Its first parameter is conventionally named self, which refers to the current instance.
Instance methods can access and modify instance attributes and can also access class attributes through the instance or class.
Example: Instance Method
class Student:
def __init__(self, name, marks):
self.name = name
self.marks = marks
def display_details(self):
print("Name:", self.name)
print("Marks:", self.marks)
def update_marks(self, new_marks):
self.marks = new_marks
student = Student("Amit", 85)
student.display_details()
student.update_marks(92)
student.display_details()
Output
Name: Amit
Marks: 85
Name: Amit
Marks: 92
Explanation
__init__() initializes the student's attributes.
display_details() reads information from the current object.
update_marks() modifies the current object's marks.
- The
self parameter allows each student object to maintain its own data.
5. Understanding Class Methods
A class method is a method associated with the class rather than a particular instance. It is defined using the @classmethod decorator, and Python automatically passes the class as the first argument, conventionally named cls.
Class methods are useful when an operation needs to read or modify class-level attributes or create instances using alternative construction logic.
Syntax
class ClassName:
class_attribute = "value"
@classmethod
def method_name(cls):
return cls.class_attribute
Example: Accessing Class Variables
class Employee:
company = "Tech Solutions"
employee_count = 0
def __init__(self, name):
self.name = name
Employee.employee_count += 1
@classmethod
def display_company(cls):
print("Company:", cls.company)
print("Employees:", cls.employee_count)
employee1 = Employee("Ravi")
employee2 = Employee("Priya")
Employee.display_company()
Output
Company: Tech Solutions
Employees: 2
Explanation
company and employee_count are class attributes.
- The
@classmethod decorator identifies display_company() as a class method.
- The
cls parameter refers to the class on which the method is called.
- The method can be called using
Employee.display_company() without creating another object.
Important: In this example, the constructor updates Employee.employee_count directly. In inheritance-aware code, use type(self).employee_count or a carefully designed class method when subclass-specific counting is required.
6. Using Class Methods to Create Objects
Class methods are frequently used as alternative constructors. An alternative constructor provides another way to create an object when the input data is supplied in a different format.
Example: Alternative Constructor
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
@classmethod
def from_string(cls, product_data):
name, price = product_data.split(",")
return cls(name.strip(), float(price.strip()))
product = Product.from_string("Laptop, 55000")
print(product.name)
print(product.price)
Output
Laptop
55000.0
Explanation
from_string() accepts product information as a single string.
- The method splits the string into a name and price.
cls(...) creates an object using the class passed to the method.
- This pattern can support subclasses because
cls refers to the class used to call the method.
When to Use Alternative Constructors
- Creating objects from CSV rows.
- Loading configuration values from strings.
- Building objects from dictionaries or API responses.
- Creating objects from database records.
- Supporting multiple input formats for the same class.
7. Understanding Static Methods
A static method is a function placed inside a class for organizational convenience. It is defined using the @staticmethod decorator.
Unlike instance and class methods, a static method does not automatically receive self or cls. It behaves like a regular function while remaining accessible through the class or its instances.
Syntax
class ClassName:
@staticmethod
def utility_method(value):
return value
Example: Static Method for Validation
class NumberUtility:
@staticmethod
def is_even(number):
return number % 2 == 0
print(NumberUtility.is_even(10))
print(NumberUtility.is_even(7))
Output
True
False
Explanation
is_even() checks whether a number is divisible by two.
- The method does not need instance attributes.
- It does not need to access class attributes.
- The
@staticmethod decorator makes its intended role clear.
8. Practical Example of a Static Method
Consider an online shopping application that needs to calculate a discount percentage. The calculation depends only on the values passed to the method.
class ShoppingCart:
@staticmethod
def calculate_discount(price, discount_percent):
if price < 0:
raise ValueError("Price cannot be negative")
if not 0 <= discount_percent <= 100:
raise ValueError("Discount must be between 0 and 100")
return price * discount_percent / 100
discount = ShoppingCart.calculate_discount(2000, 15)
print("Discount:", discount)
print("Final price:", 2000 - discount)
Output
Discount: 300.0
Final price: 1700.0
This method is suitable as a static method because the calculation does not depend on a particular shopping cart or class-level state.
9. Difference Between Class Methods and Static Methods
| Feature |
Class Method |
Static Method |
| Decorator |
@classmethod |
@staticmethod |
| First argument |
cls |
No automatic argument |
| Receives class automatically |
Yes |
No |
| Receives instance automatically |
No |
No |
| Can access class attributes |
Directly through cls |
Only if the class or its data is explicitly supplied or otherwise referenced |
| Common use |
Alternative constructors and class-level operations |
Validation, conversion, and utility calculations |
10. Understanding Decorators in Python
A decorator is a callable that takes another function or callable and returns a function or callable, often with additional behavior. Decorators allow developers to extend or modify behavior without rewriting the original function's main logic.
Decorators are widely used for logging, authorization, timing, caching, validation, registration, and method definitions such as @classmethod, @staticmethod, and @property.
Basic Decorator Syntax
def decorator_function(original_function):
def wrapper():
print("Before function execution")
original_function()
print("After function execution")
return wrapper
@decorator_function
def greet():
print("Hello, Python!")
greet()
Output
Before function execution
Hello, Python!
After function execution
How It Works
- The function
greet() is defined.
- The decorator receives the original function.
- The decorator returns the wrapper function.
- When
greet() is called, the wrapper runs the additional statements and calls the original function.
The syntax @decorator_function is equivalent to assigning the decorated result back to the original name, as in greet = decorator_function(greet).
11. Creating Decorators That Accept Arguments
In real applications, decorated functions often accept parameters. A reusable decorator should accept arbitrary positional and keyword arguments using *args and **kwargs.
from functools import wraps
def log_call(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Calling:", func.__name__)
result = func(*args, **kwargs)
print("Function completed")
return result
return wrapper
@log_call
def add(a, b):
return a + b
result = add(10, 20)
print("Result:", result)
Output
Calling: add
Function completed
Result: 30
Explanation
*args accepts positional arguments.
**kwargs accepts keyword arguments.
func(*args, **kwargs) forwards the supplied arguments to the original function.
return result preserves the original function's return value.
@wraps(func) preserves useful metadata such as the original function's name and docstring.
12. Using Decorators for Execution Time
Decorators can measure how long a function takes to execute. This is useful when profiling application performance or identifying slow operations.
import time
from functools import wraps
def measure_time(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
try:
return func(*args, **kwargs)
finally:
elapsed = time.perf_counter() - start
print(f"Execution time: {elapsed:.6f} seconds")
return wrapper
@measure_time
def calculate_sum():
return sum(range(100000))
result = calculate_sum()
print("Calculation completed:", result)
Explanation
time.perf_counter() provides a high-resolution timer suitable for measuring elapsed time.
- The decorator measures the duration of the function call.
- The
finally block reports elapsed time even if the decorated function raises an exception.
- The wrapper returns the original result when the function completes successfully.
Note: The measured duration varies depending on the computer, system load, and execution environment.
13. Decorators With Their Own Arguments
A parameterized decorator allows you to configure the behavior of the decorator itself. This typically requires three nested functions: one to receive the decorator arguments, one to receive the original function, and one to execute the wrapped function.
from functools import wraps
def repeat(times):
if times < 0:
raise ValueError("times cannot be negative")
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
result = None
for _ in range(times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator
@repeat(3)
def say_hello():
print("Hello!")
say_hello()
Output
Hello!
Hello!
Hello!
Explanation
repeat(3) configures the decorator to execute the function three times.
decorator() receives the function being decorated.
wrapper() controls each execution.
- The final result is the result of the last execution, or
None when the function is not executed.
14. Applying Multiple Decorators
Python allows multiple decorators to be applied to the same function. They are applied from the closest decorator to the function outward.
from functools import wraps
def bold(func):
@wraps(func)
def wrapper():
return "<b>" + func() + "</b>"
return wrapper
def italic(func):
@wraps(func)
def wrapper():
return "<i>" + func() + "</i>"
return wrapper
@bold
@italic
def message():
return "Hello Python"
print(message())
Output
<b><i>Hello Python</i></b>
Explanation
The decorator stack is equivalent to message = bold(italic(message)). The italic decorator wraps the original function first, and bold wraps that result.
15. Built-in Decorators in Python
Python includes several built-in decorators that support object-oriented programming and other common development tasks.
| Decorator |
Purpose |
Example Use |
@classmethod |
Creates a method receiving the class as its first argument. |
Alternative constructors |
@staticmethod |
Defines a method without automatic instance or class arguments. |
Utility calculations |
@property |
Allows a method to be accessed like an attribute. |
Computed values and controlled attribute access |
@functools.wraps |
Preserves metadata when writing wrappers. |
Custom decorators |
@functools.lru_cache |
Caches results of calls based on their arguments. |
Repeated, expensive calculations |
@dataclasses.dataclass |
Generates common special methods for data-focused classes. |
Data models |
16. Using the @property Decorator With Methods
The @property decorator turns a method into an attribute-like interface. It is useful for calculating values or validating updates without requiring callers to use explicit getter and setter method names.
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@property
def area(self):
return self.length * self.width
rectangle = Rectangle(10, 5)
print(rectangle.area)
Output
50
Notice that rectangle.area is accessed without parentheses. The property provides a clean interface while performing the calculation when accessed.
17. Choosing the Right Method Type
Choosing the appropriate method type makes class design easier to understand and maintain.
- Use an instance method when the operation depends on a particular object's data.
- Use a class method when the operation needs the class itself or should create an instance using
cls.
- Use a static method when the operation logically belongs to the class but does not need automatic access to the instance or class.
- Use a custom decorator when additional behavior should be applied consistently to one or more functions or methods.
Example Combining All Three Method Types
class Temperature:
unit = "Celsius"
def __init__(self, value):
self.value = value
def display(self):
return f"{self.value} {type(self).unit}"
@classmethod
def from_fahrenheit(cls, value):
celsius = (value - 32) * 5 / 9
return cls(celsius)
@staticmethod
def is_valid(value):
return isinstance(value, (int, float))
temperature = Temperature(25)
print(temperature.display())
converted = Temperature.from_fahrenheit(98.6)
print(round(converted.value, 1))
print(Temperature.is_valid(30))
Output
25 Celsius
37.0
True
18. Common Mistakes and Troubleshooting
| Mistake |
Why It Happens |
Solution |
Using self in a class method |
Class methods do not automatically receive an instance. |
Use cls or call an instance method on an object. |
Using cls in a static method without defining it |
Static methods receive no automatic class argument. |
Pass the class explicitly or use a class method. |
| Forgetting to return the wrapper |
The decorated function may become None. |
Return the wrapper from the decorator. |
| Not forwarding arguments |
The wrapper may fail when called with parameters. |
Use *args and **kwargs when appropriate. |
| Dropping the return value |
The wrapper may discard the original function's result. |
Return the result of calling the wrapped function. |
Skipping @wraps |
Function metadata may describe the wrapper instead of the original function. |
Use functools.wraps in custom decorators. |
| Choosing a static method unnecessarily |
The method may actually need class or instance state. |
Choose the method type based on the data it needs. |
19. Best Practices
- Choose meaningful method names that explain their purpose.
- Use
cls consistently as the first parameter of class methods.
- Keep static methods focused on a single utility task.
- Use class methods for alternative constructors that should respect inheritance.
- Use
functools.wraps when implementing decorators that wrap functions.
- Preserve function arguments, return values, and exceptions unless intentionally changing the behavior.
- Keep decorators small and reusable.
- Document decorators that change function execution, caching, permissions, or error handling.
- Write unit tests for methods and decorators, including edge cases and invalid inputs.
- Avoid unnecessary decorators when a normal function or method would be clearer.
20. Practical Exercises
Exercise 1: Class Method
Create a BankAccount class with a class attribute called bank_name. Define a class method that displays the bank name.
Expected learning: Accessing class-level attributes using cls.
Exercise 2: Alternative Constructor
Create a Book class with title and author attributes. Implement a class method that creates a book from a string formatted as "Title, Author".
Expected learning: Using a class method to parse data and construct objects.
Exercise 3: Static Method
Create a MathHelper class with a static method that checks whether a number is prime.
Expected learning: Writing a utility method that does not depend on object state.
Exercise 4: Logging Decorator
Create a decorator that prints the name of a function before executing it. Apply the decorator to two different functions.
Expected learning: Reusing decorators across multiple functions.
Exercise 5: Retry Decorator
Write a parameterized decorator that retries a function a specified number of times when it raises an exception. Re-raise the final exception if all attempts fail.
Expected learning: Handling exceptions and controlling repeated function execution.
21. Mini Project: Employee Management System
Develop a small employee management application using instance methods, class methods, static methods, and a custom decorator.
Project Requirements
- Create an
Employee class with employee ID, name, and salary.
- Use an instance method to display employee details.
- Use a class method to create an employee from a string.
- Use a static method to validate whether a salary is a positive number.
- Create a decorator that logs when employee information is displayed.
- Test the program with multiple employee objects.
Sample Implementation
from functools import wraps
def log_action(func):
@wraps(func)
def wrapper(*args, **kwargs):
print("Action:", func.__name__)
return func(*args, **kwargs)
return wrapper
class Employee:
company = "ABC Technologies"
def __init__(self, employee_id, name, salary):
if not self.is_valid_salary(salary):
raise ValueError("Salary must be a positive number")
self.employee_id = employee_id
self.name = name
self.salary = salary
@classmethod
def from_string(cls, data):
employee_id, name, salary = data.split(",")
return cls(
employee_id.strip(),
name.strip(),
float(salary.strip())
)
@staticmethod
def is_valid_salary(salary):
return (
isinstance(salary, (int, float))
and not isinstance(salary, bool)
and salary > 0
)
@log_action
def display_details(self):
print("Company:", type(self).company)
print("Employee ID:", self.employee_id)
print("Name:", self.name)
print("Salary:", self.salary)
employee = Employee.from_string("E101, Neha, 45000")
employee.display_details()
Sample Output
Action: display_details
Company: ABC Technologies
Employee ID: E101
Name: Neha
Salary: 45000.0
Project Concepts Demonstrated
- Instance methods manage individual employee information.
- The class method creates an employee from a string.
- The static method validates salary input.
- The custom decorator adds logging behavior to a method.
- Exception handling prevents the creation of employees with invalid salaries.
22. Interview Questions and Answers
Q1. What is a class method in Python?
A class method is a method decorated with @classmethod that receives the class as its first argument, conventionally named cls.
Q2. What is the purpose of the @staticmethod decorator?
It defines a method that does not automatically receive an instance or class argument. It is useful for utility functions logically associated with a class.
Q3. What is the difference between self and cls?
self conventionally refers to an individual instance, while cls refers to the class on which a class method is called.
Q4. Can a class method access instance variables?
Not directly through an automatically supplied instance, because a class method receives the class rather than an instance. It can access instance data if an instance is explicitly passed to it.
Q5. Can a static method access class variables?
A static method has no automatic cls parameter, but it can access class variables if the class is referenced explicitly or passed as an argument.
Q6. What is a decorator?
A decorator is a callable that takes another callable and returns a callable, often adding behavior around the original operation.
Q7. What does @classmethod do internally?
It creates a class method descriptor. When accessed through a class or instance, Python binds the method to the appropriate class and supplies that class as the first argument.
Q8. What does @staticmethod do?
It creates a static method descriptor so that accessing the method through a class or instance does not automatically bind an instance or class argument.
Q9. Why is functools.wraps used?
It preserves important metadata from the original function, including its name, documentation string, and wrapped-function reference.
Q10. What is a parameterized decorator?
It is a decorator configured with arguments, such as a repeat count or a logging option, before it receives the function being decorated.
Q11. Can decorators be applied to class methods?
Yes. Custom decorators can wrap class methods, but the order of decorators matters. A common pattern is to apply a custom function decorator above @classmethod, so it receives the bound method appropriately.
Q12. When should you use a class method instead of a static method?
Use a class method when the implementation needs the class itself, particularly for class-level operations and alternative constructors. Use a static method when no automatic class or instance reference is needed.
23. Quick Revision Checklist
- Instance method: Uses
self to work with an instance.
- Class method: Uses
@classmethod and receives cls.
- Static method: Uses
@staticmethod without an automatic first argument.
- Decorator: Extends or modifies the behavior of a callable.
- Wrapper: A function that adds behavior around another function.
- @wraps: Preserves metadata from the original function.
- Alternative constructor: A class method that creates an instance using another input format.
- Best practice: Select the method type according to the data and behavior required.
24. Conclusion
Class methods, static methods, and decorators are important features of Python that help developers build reusable and maintainable applications. Instance methods operate on individual objects, class methods operate with class-level context, and static methods provide related utilities without automatic instance or class binding.
Decorators extend this flexibility by adding reusable behavior such as logging, timing, validation, and execution control. By practicing these concepts through small programs and projects, learners can strengthen their understanding of Python object-oriented programming and write clearer, more professional code.
To continue learning Python programming, explore the Python Training Course at JustAcademy, visit the JustAcademy website, or register for a course demo.