• Ian Stuart's picture

    Understanding Python decorators - with the help of a code visualiser!

    Ian Stuart / July 30, 2018
  • Python has this funky thing called "decorators" - you can think of them as inverting the "function inside a function" concept, by wrapping a function around a function.

    Confused?

    Me too!

    Let me see if I can explain it (after all, there's only a million other people got pages explaing this :) )

    Two basic concepts first:

        def foo(x):
            print("Hi, foo has been called with " + str(x))
    
        print("Call foo")
        foo("Hi")
    
        bar = foo
        print("Call foo via bar")
        bar("Hi")
    So,
    1. we can call a function, and
    2. we can assign a function-call to a variable, and call it via that.

    Functions as parameters:

    The next one to wrap your head around is having a function that takes another function as a parameter.... thus:

        def sports_car():
            print("I'm a sports car")
    
        def trailer(func):
            print('I'm a trailer')
            print('I'll check my contents now')
            func()
            print("func's real name is " + func.__name__)
    
        trailer(sports_car)
    

    This will output the following:

        I'm a trailer
        I'll check my contents now
        I'm a sports car
        func's real name is sports_car

    What Decorators do is allow us to take sports_car, and wrap trailer around it - thus:

        def sports_car(name):
            print(f"I'm a sports car, and my name is {name}")
    
        def trailer(func):
            def wrapper(x):
                print(func.__name__ + ' must load onto a trailer')
                func(x)
                print(func.__name__ + ' has been unload')
            return wrapper
    
        sports_car('Lightning McQueen')
        sports_car = trailer(sports_car)
        sports_car('Lightning McQueen')

    This will output the following:

        I'm a sports car, and my name is Lightning McQueen
        sports_car must load onto a trailer
        I'm a sports car, and my name is Lightning McQueen
        sports_car has been unload

    The best tool for seeing how this all works: http://www.pythontutor.com/visualize.html - cut'n'paste the code in, and step through it :)