Python Decorators

A Python Decorator provides a concise and reusable way for extending a function or a class.

Bare bone decorator

A decorator in its simplest form is a function that takes another function as an argument and returns a wrapper. The following example shows the creation of a decorator and its usage.

def your_decorator(func):
  def wrapper():
    # Do stuff before func...
    print("Before func!")
    func()
    # Do stuff after func...
    print("After func!")
  return wrapper

@your_decorator
def foo():
  print("Hello World!")

foo()
# Before func!
# Hello World!
# After func!

Decorator for a function with parameters

def your_decorator(func):
  def wrapper(*args,**kwargs):
    # Do stuff before func...
    print("Before func!") 
    func(*args,**kwargs)
    # Do stuff after func...
    print("After func!")
  return wrapper

@your_decorator
def foo(bar):
  print("My name is " + bar)

foo("Jack")

# Before func!
# My name is Jack
# After func!

Template for a basic decorator

This template is useful for most decorator use-cases. It is valid for functions with or without parameters, and with or without a return value.

import functools

def your_decorator(func):
  @functools.wraps(func) # For preserving the metadata of func.
  def wrapper(*args,**kwargs):
    # Do stuff before func...
    result = func(*args,**kwargs)
    # Do stuff after func..
    return result
  return wrapper

Decorator with parameters

You can also define parameters for the decorator to use.

import functools

def your_decorator(arg):
  def decorator(func):
    @functools.wraps(func) # For preserving the metadata of func.
    def wrapper(*args,**kwargs):
      # Do stuff before func possibly using arg...
      result = func(*args,**kwargs)
      # Do stuff after func possibly using arg...
      return result
    return wrapper
  return decorator

To use this decorator:

@your_decorator(arg = 'x')
def foo(bar):
  return bar

Class based decorators

To decorate a class methos, you must define the decorator within the class. When only the implicit argument self is passed to the method, without any other additional arguments, you must make a separate decorator for only those methods without any additional arguments. An example of this is when you want to catch and print exceptions in a certain way.

class DecorateMyMethod:

  def decorator_for_class_method_with_no_args(method):
    def wrapper_for_class_method(self)
      try:
        return method(self)
      except Exception as e:
        print("\nWARNING: Please make note of the following:\n")
        print(e)
    return wrapper_for_class_method

  def __init__(self,succeed:bool):
    self.succeed = succeed

  @decorator_for_class_method_with_no_args
  def class_action(self):
    if self.succeed:
      print("You succeeded by choice.")
    else:
      raise Exception("Epic fail of your own creation.")

test_succeed = DecorateMyMethods(True)
test_succeed.class_action()
# You succeeded by choice.

test_fail = DecorateMyMethod(False)
test_fail.class_action()
# Exception: Epic fail of your own creation.

A decorator can also be defined as a class instead of a method. This is useful for maintaining and updating a state, such as in the following example, where we count the number of calls made to a method:

class CountCallNumber:

  def __init__(self, func):
    self.func = func
    self.call_number = 0

  def __call__(self, *args, **kwargs):
    self.call_number += 1
    print("This is execution number " + str(self.call_number))
    return self.func(*args, **kwargs)

@CountCallNumber
def say_hi(name):
  print("Hi! My name is " + name)

say_hi("Jack")
# This is execution number 1
# Hi! My name is Jack

say_hi("James")
# This is execution number 2
# Hi! My name is James

Count Example

This count example is inspired by Patrick Loeber's YouTube tutorial.

Subscribe to pythoncheatsheet.org

Join 14,100+ Python developers in a two times a month and bullshit free publication , full of interesting, relevant links.