Class Decorators in Python (56/100 Days of Python)
Besides using decorators to modify function behaviors, in Python, decorators can also be used to modify the behavior of classes. Class decorators are functions that take a class as an argument, modify the class in some way, and then return the modified class.
Class decorators can be used to add new attributes or methods to a class, modify existing attributes or methods, or even replace the entire class with a new implementation. In this tutorial, we’ll cover some common use cases for class decorators and how to create your own.
Adding New Attributes Or Methods To a Class
One common use case for class decorators is to add new attributes or methods to a class. For example, you might want to add a new class-level attribute that keeps track of the number of instances created, or add a new method that provides some additional functionality:
def add_class_counter(cls):
cls.num_instances = 0
orig_init = cls.__init__
def new_init(self, *args, **kwargs):
cls.num_instances += 1
orig_init(self, *args, **kwargs)
cls.__init__ = new_init
return cls
In this example, add_class_counter
is a class decorator that takes a class cls
as an argument. The decorator adds a new class-level attribute num_instances
and a new __init__
method that increments the num_instances
attribute every time a new instance is created.
You can apply this decorator to a class by adding the decorator at the top of a class:
@add_class_counter
class MyClass:
def __init__(self, name):
self.name = name
Now, every time you create a new instance of MyClass
, the num_instances
attribute is incremented:
a = MyClass('Alice')
b = MyClass('Bob')
print(MyClass.num_instances) # 2
Modifying Existing Attributes Or Methods Of a Class
Another use case for class decorators is to modify existing attributes or methods of a class. For instance, you might want to add some additional behavior to an existing method or change the behavior of an existing attribute:
def add_logging_to_method(method):
def new_method(self, *args, **kwargs):
print(f'Calling method {method.__name__}')
return method(self, *args, **kwargs)
return new_method
def add_logging_to_all_methods(cls):
for name, method in vars(cls).items():
if callable(method):
setattr(cls, name, add_logging_to_method(method))
return cls
In this example, add_logging_to_method
is a function that takes a method as an argument and returns a new method that logs information about the method call before calling the original method. add_logging_to_all_methods
is a class decorator that modifies all methods of a class by applying the add_logging_to_method
decorator to each one.
@add_logging_to_all_methods
class MyClass:
def __init__(self, name):
self.name = name
def say_hello(self):
print(f'Hello, {self.name}!')
Now, every time you call the say_hello
method, information about the method call is logged:
a = MyClass('Alice')
a.say_hello()
# Calling method say_hello
# Hello, Alice!
What’s next?
- If you found this story valuable, please consider clapping multiple times (this really helps a lot!)
- Hands-on Practice: Free Python Course
- Full series: 100 Days of Python
- Previous topic: Implementing Custom Decorator Functions
- Next topic: Python — Exception Handling