Python Decorators

1 minute read

Decorators let us extend a function’s behaviour.

def big_red(f):
    def wrapper(*args, **kwargs):
        print('\x1b[1;31m', end='')
        f(*args, **kwargs)
        print('\x1b[0m', end='')
    return wrapper

def hello_number(value):
    print(f"Hello {value}!")


big_red makes a function’s output turn bold and red.

The directive @big_red is the equivalent to:

def hello_number(value):
    print(f"Hello {value}!")

hello_number = big_red(hello_number)

Decorating Classes

The next example does the following:

  • decorates a class rather than a function
  • takes an argument
def Family(surname):
    def decorator_family(cls):
        class Wrapper(cls):
            def __init__(self, name):
       = + " " + surname

        return Wrapper
    return decorator_family

class Person:
    def __init__(self, name): = name

    def greet(self):
        print(f"Hello, I am {}")


The output is:

Hello, I am Steve Jones

Classes that Decorate

Sometimes you want to write a class that decorates a function (or another class). This is useful if you want to make your decoration stateful.

def Limit(max_calls=2):

    class Limit:

        def __init__(self, function):
            self.function = function
            self.calls = max_calls

        def __call__(self, *params):
            if self.calls <= 0:
                raise RuntimeError("Limit reached!")
                self.calls = self.calls - 1
                return self.function(*params)
    return Limit

def guess_number(guess):
    if guess == 3:
        print("You guessed correctly")
        print("Bad guess!")


The output should read:

Bad guess!
Bad guess!
Traceback (most recent call last):
  File "/Users/hoani/sandbox/python/", line 29, in <module>
  File "/Users/hoani/sandbox/python/", line 12, in __call__
    raise RuntimeError("Limit reached!")
RuntimeError: Limit reached!

But if we change the decorator to @Limit(max_calls=3); then we get:

Bad guess!
Bad guess!
You guessed correctly