There are many tutorials about how to decorate a function in Python language. In short, we do this as follows:

def my_decorator(func):
	def wrapper(*args, **kwargs):
		wrapped_results = []
		wrapped_results.append(">>>")
		wrapped_results.append(func(*args, **kwargs))
		wrapped_results.append("<<<")
		return "\n".join(wrapped_results)
	return wrapper


@my_decorator
def hello(name) -> str:
	return f"Hello {name}!"

print(hello("Kamil"))

"""
Prints:
>>>
Hello Kamil!
<<<
"""

Suppose we would like to decorate the whole class instead of a single method. How to do this? Let’s see next listing and find the difference from the first example:

def AddMethodDecorator(klass):
	"""Definition of your decorator"""

	def new_method(self, *args, **kwargs):
		"""Define your new method here"""
		print("Args:")
		for arg in args:
			print(arg)

		print("Kwargs:")
		for (key, value) in enumerate(kwargs):
			print(f"{key}: {value}")

	setattr(klass, "new_method", new_method)
	return klass


@AddMethodDecorator
class MyClass:

	def foo(self):
		print("bar")


my_class_obj = MyClass()
my_class_obj.new_method('a', b=2)

"""
Prints:
Args:
a
Kwargs:
0: b
"""

First of all, we decorate class, not function, so our decorator takes argument klass instead of func as you see in line 1. You may wonder why I didn’t use cls name for decorated class. It’s because by convention I use cls in the context of class methods.

In next lines goes the definition of your method. As you see, it’s the first argument is self, so you will get access to all fields and methods of your object.

The most important thing happens in lines 14 and 15, where we command Python to attach the new method to the class. Finally, we return our extended class like we did this with decorating a method in the first code snippet.