Python: Decorator Tutorial

By Xah Lee. Date:

What is Decorator?

Python “decorator” is a language construct that lets you define a wrapper function g to another function f, such that, when f is called, your wrapper g is called instead. (it can also be applied to methods, and class.)

The syntax for “decorator” is @name1 immediately before the line of def name2.

Here's a decorator:

@g
def f():
    print("xyz")

is equivalent to

def f():
    print("xyz")
f = g(f)

That is ALL there is of Python decorators.

Note that:

  1. when the function f is called, the wrapper g will be called instead.
  2. the wrapper function g receives f as its argument.
  3. the wrapper function g must return a function. Because, remember, the value of f is now g(f), and f is a function. So, when f is called such as f(3), Python evaluates g(f)(3), so g(f) must be a function too. (technically, it just need to be anything callable.)

Here's a example of decorator:

# -*- coding: utf-8 -*-
# python 2

# example of a decorator

def gg(xx):
    print "gg called"
    def s(y):
        return y-1
    return s

@gg  # ← this is decorator
def ff(x):
    print "ff called"
    return x+1

print ff(3)

# output:
# gg called
# 2

Note: the wrapper function gg receives the function ff. The wrapper can do anything with it. Typically, it'll eventually call ff, but it doesn't have to.

In the above example, the wrapper gg bypass ff entirely.

Checklist for Writing a Decorator

Here's 4 things to remember when writing a decorator:

  1. The wrapper function g must take a function as argument. (because it'll receive f as argument.)
  2. The wrapper function g must return a function. (because that'll be applied to f's arguments.)
  3. The returned function h must take the same number/type of arguments as f.
  4. OFTEN, the returned function h should return the same type of value that f returns. If f returns a string, h probably should too. If f returns None, h probably should too.

Decorator Examples

Decorator Example: Check/Modify Input Argument

Here's a example of decorator. It (in effect) modifies the function's argument.

# -*- coding: utf-8 -*-
# python 2

# example of a decorator

def gg(func):
    """a decorator for ff. Make sure input to ff is always even. If not, add 1 to make it even"""
    def hh(y):
        if y % 2 == 0:    # even
            return func(y)
        else:
            return func(y+1)
    return hh

@gg
def ff(x):
    return x

print ff(3) # 4
print ff(4) # 4
print ff(5) # 6
print ff(6) # 6

Note: decorator function gg does not know what the decorated function ff's arguments. However, since gg is a wrapper, it can create and return a new function hh, and have hh check arguments then call ff(args). This works because whatever arguments passed to the original function ff is passed to hh, then hh can do arg checking, then call ff.

Decorator Example: Catch All Arguments (And Run Conditionally)

When defining the decorator function, often you need to catch all possible arguments of the original function. Here's a example of how.

In the following example, the original function is called only if some global variable is true.

# -*- coding: utf-8 -*-
# python 2

# example of decorator that check condition to decide whether to call

cc = True   # ← the condition

def gg(func):
    """a decorator for ff. If cc is true, run ff, else do nothing"""
    def hh(*args, **kwargs): # ← param must catch all args of ff
        """do nothing function"""
        pass
    if cc:
        return func
    else:
        return hh

@gg
def ff(*args, **kwargs):
    # ff can be sending email, or any callable with no return value
    print "ff"
    pass

ff(3)
ff(3,"Jane")
ff(3,4, k="thank you")

# if cc is true, then ff is called 3 times
# (different set of arguments are used, to illustrate that our wrapper work well with them)

# if cc is false, Nothing's done, ff is not called.

[see Python: Function]

Decorator with Parameters

Decorator itself can have parameters.

For example, the following decorator:

@g(3)
def f():
    print("xyz")

is equivalent to

def f():
    print("xyz")
f = g(3)(f)

There's nothing special about this. In the simplest decorator example, @g followed by def f: gets transformed into f = g(f). Now, if we replace g by g(x), we get f = g(x)(f).

Decorators can get complex, because it's transforming code and doing function applications and involves nested functions. In this case, just remember that:

Example:

# -*- coding: utf-8 -*-
# python 2

# example of decorator with argument

def gg(num):
    """a decorator for ff. Ignore ff. Simply return num."""
    def h1(f1):
        def h2(f2):
            return num
        return h2
    return h1

# gg(1) must return a function h1, such that h1 accept function (the ff), and also return a function (to be applied to ff's args)
@gg(1)
def ff(x):
    return repr(x) + " rabbits"

print ff(3) # 1
print ff(4) # 1
print ff(5) # 1

If you have a question, put $5 at patreon and message me.

Python

  1. Python 3 Basics
  2. Python 2 Basics
  3. Python 2 and 3 Difference
  4. Print Version
  5. Builtin Help
  6. Quote String
  7. String Methods
  8. Format String
  9. Operators
  10. True, False
  11. if then else
  12. Loop
  13. List Basics
  14. Loop Thru List
  15. Map f to List
  16. List Comprehension
  17. List Methods
  18. Sort
  19. Dictionary
  20. Loop Thru Dict
  21. Dict Methods
  22. Function
  23. Class
  24. List Modules
  25. Write a Module
  26. Unicode 🐍
  27. Object, ID, Type

Regex

  1. Regex Basics
  2. Regex Reference

Text Processing

  1. Read/Write File
  2. Traverse Directory
  3. 2 Traverse Directory
  4. Manipulate Path
  5. Process Unicode
  6. Convert File Encoding
  7. Find Replace in dir
  8. Find Replace by Regex
  9. Count Word Frequency

Web

  1. Send Email
  2. GET Web Page
  3. Web Crawler
  4. HTTP POST

Misc

  1. JSON
  2. Find Script Path
  3. Get Env Var
  4. System Call
  5. Decompress Gzip
  6. Complex Numbers
  7. Copy Nested List
  8. Tuple vs List
  9. Sets, Union, Intersection
  10. Closure
  11. 2 Closure
  12. Decorator
  13. 3 Map with Side Effect
  14. Append String in Loop
  15. Timing f timeit
  16. Keyword Arg Default Value Unstable
  17. Check Page Load Size
  18. Thumbnail Generation