DEV Community

Cover image for Python Decorator Quick-Reference
Tai Kedzierski
Tai Kedzierski

Posted on • Updated on

Python Decorator Quick-Reference

(Image (C) Tai Kedzierski)

There are plenty of lenghty tutorial posts out there about what Python decorators are, and the step by step incremental construction of one.

But sometimes all we need is a simple quick refresher. Here's mine (also on Gist).

Basic concepts

A Python decorator is a special function that can be added above a callable definition (function, class, anything that implements the __call__() method)

When a decorated function is called, it is in fact the decorator itself that is called, and the wrapped callable is then normally called by the implementing handler of the decorator.

A Python decorator is comprised of 3 parts:

  • name
  • wrapped function receiver
  • decorator implementation handler

A simple decorator

In a simple decorator, the name and the function receiver are one. It supplies a handling function, which should call the wrapped function, then return its result. The naming function must return the handler function.

def printres(func): # decorator name, wrapped function receiver

    def handler(*args, **kwargs): # decorator handling function
        res = func(*args, **kwargs)
        print("{}: {}".format("Result", res))
        return res
    return handler

Enter fullscreen mode Exit fullscreen mode

A decorator with parameters

In a parameterised decorator, the name and receiver are separated into two functions. The outermost function names the decorator and takes its parameters. The first inner function is a wrapper to receive the target function.

We need to import functools and use its 'wraps' decorator to munge the handler, since it is displaced by 1 level, and can cause oddities when stack traces are raised, making debugging difficult. The inner function returns the handler, and the outer function returns the inner function receiver.

import functools

def printmsg(message): # decorator name, decorator parameters

    def function_receiver(func): # wrapped function receiver
        @functools.wraps(func) # munge func so that stack traces and call sequences are retained

        def handler(*args, **kwargs): # decorator handling function
            res = func(*args, **kwargs)
            print("{}: {}".format(message, res))
            return res
        return handler
    return function_receiver
Enter fullscreen mode Exit fullscreen mode

Example usage

@printres
def plus(x, y):
    return x+y


@printmsg("minus")
def minus(x, y):
    return x-y


z1 = plus(2,3)
z2 = minus(3,6)

print("Final print: {} // {} ".format(z1,z2))
Enter fullscreen mode Exit fullscreen mode

There you go. Quick, visual, easy. Hope this helps someone ...!

Top comments (0)