Decorators (Advanced topics)#

BB1000 Programming in Python


layout: false

Decorators in Python: basic concepts#

  • Python function definition

def function_name(parameters):
    "function docstring"
    function body
    return [expression]
  • Python class method definition

def methodname(self, parameters):
    "method docstring"
    method body
    return [expression]
  • Functions are first-class objects:

    • implemented operators can be applied to function variable

    • functions can be passed as arguments to other functions

    • functions can be used as class/object variables in Class


Decorators in Python: basic concepts#

  • passing arguments to function via args-kwargs syntax

def function_name(*args, **kwargs):
    "function docstring"
    function body
    return[expression]
  • args are positional parameters of function (tuple)

  • kwargs are key-valued parameters of the function (dict)

The function call

>>> function_name('hello', 'world', when='now')

will map values to variables in the function

args -> ('hello', 'world')  # tuple
kwargs -> {'when': 'now'}  # dict

  • Definition: a decorator extends and modified the behaviour of a callable (functions, methods, and classes) without permantly modifying the callable itself

  • A decorator is a function that takes a function as in put and returns a function as output

def decorator(f):
    ...
    
@decorator
def function_name(...):
    ...

is equivalent to

def decorator(f):
    ...
    
def function_name(...):
    ...
    
    
function_name = decorator(function_name)

def empty_decorator(func):
    return func
    
def hello():
    return "Hello world"
    
simple_hello = empty_decorator(hello)

>>> simple_hello()
Hello world!
  • Returning modified function from decorator

def uppercase(func):
    def wrapper():
        original_result = func()
        modified_result = original_result.upper()
        return modified_result
    return wrapper
    
@uppercase
def hello():
    return "Hello world!"
    
>>> hello()
HELLO WORLD!

  • Multiple decorators applied to single function

def strong(func):
    def wrapper():
        return '**' + func() + '**'
    return wrapper
    
def emphasis(func):
    def wrapper():
        return'!!' + func() + '!!'
    return wrapper
    
@emphasis
@strong
def hello():
    return'Hello World'
    
>>> hello()
!!**Hello World**!!

  • Passing variables to decorator

def decorator_function(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

  • Example: inspect values passed to and from a function

from functools import wraps

def trace(func):
    @wraps
    def wrapper(*args, **kwargs):
        print(
            f'TRACE: calling {func.__name__}() '
            f'with {args}, {kwargs}'
        )
        original_result = func(*args, **kwargs)
        print(
            f'TRACE: {func.__name__}() '
            f'returned {original_result!r}'
        )
        return original_results
    return wrapper
  • Apply trace decorator to function

@trace
def say(greet, name):
    return greet + ",' +  name
>>> say("Hello", "world")
TRACE: calling say() with ('Hello', 'world'), {}
TRACE: say() returned 'Hello, world'
Hello, world

  • Decorate a class method

def as_html(func):
    def func_wrapper(*args, **kwargs):
        return f"<p>{func(*args, **kwargs)}</p>
    return func_wrapper
    
class Person:
    def__init__(self):
        self.name = "John"
        self.family = "Doe"
        
        
    @as_html
    def hello(self):
        return f"Hello, my name is {self.name} {self.family}"
        
>>> my_person = Person("Anne", "Applebaum")
>>> print(my_person.hello())
<p>Hello, my name is Anne Applebaum</p>

About decorators:

  • decorators is good way to avoid code duplication

  • decorators allows to write more “pythonic” and clean code

  • functools.wraps make your decorators debug friendly when using multiple decorators

See also

  • https://docs.python.org/3/glossary.html#term-decorator

  • https://realpython.com/primer-on-python-decorators/

  • https://www.geeksforgeeks.org/decorators-in-python/