Session 6 - Functions & Decorators

In Python, decorators allow you to add new functionality to your code by decorating functions or classes with additional code. This can make your code more modular and easier to maintain, as you can separate different concerns into separate decorators.

The boilerplate syntax of a decorator is like this:

1def decorator(func):
2    def wrapper_decorator(*args, **kwargs):
3            # Do something before
4            value = func(*args, **kwargs)
5            # Do something after
6            return value
7
8    return wrapper_decorator

And if you want to use it you will do like so:

1@decorator
2def greet(name):
3     return 'Hello ' + name

Learning goals

By reading the texts in the materials section, doing the 3 exercises, and follow the teachings, you will be able to explain what a decorator is, when to use it, and how the inner parts of a decorator function is made up, and you will be able to create your own, and use others already made decorators.

After this week you will know about and be able to use and explain:

  • First class functions

  • Inner functions

  • Decorator functions
    • explain how a decorator function works

    • understand what the return values and return types are of the different functions used in a decorator

    • understand why we reuse the variable names in the scope.

Materials

Exercises

Warm up exercises

Solution

Think about the each of the following functions and determain what are the:

  • return value

  • return type

  • parameter type

  • parameter value

example1

1def add():
2     pass

example2

1def add():
2     print('Hello')

example3

1def add(num):
2     return num + num

example4

1def add(*args):
2     return sum(args)

example5

1def add(*args):
2     if all(type(element) == type(args[0]) for element in args):
3             return sum(args)
4     return None

Small Exercises

Solution

With this function as a starting point

1def add(*args):
2    return sum(args)
  1. Write a decorator that writes to a log file the time stamp of each time this function is called.

  2. Change the log decorator to also printing the values of the argument together with the timestamp.

  3. Print the result of the decorated function to the log file also.

  4. Create a new function and call it printer(text) that takes a text as parameter and returns the text. Decorate it with your logfunction. Does it work?

Ex1: Time it!

Solution

Next week we will work with generators, generator expressions and list comprehensions. These topics has a lot to do with program efficiency.

For this we will be measuring our code in diffenrent ways and especialy we will ‘time it’ and ‘messure memmory usage’.

If you want to messure how much time it takes to execute a piece of code you could do the followin:

1import time
2
3start = time.time()
4// do some stuff you want to meassure here
5end = time.time()
6print(end - start)

Instead of writing this every time you need to time something, you could write a docorator function that does the job for you.

Task:

Your job is, to write a decorator function that can time any piece of code.

You can read about time by starting your interpretor and write:

> import time
> help(time)

Ex3: Slow down code

Solution

The code below counts down from n -> 0. So calling countdown(5) prints: 5 4 3 2 1 Liftoff!

1def countdown(n):
2     if not n:   # 0 is false, not false is true
3         return n
4     else:
5         print(n, end=' ')
6         return countdown(n-1) # call the same function with n as one less

(The function is a recursive function, which you might or might not have worked with before.)

Task:

Create a decorator function that slows down your code by 1 second for each step. Call this function slowdown()

For this you should use the ‘time’ module.

When you got the ‘slowdown code’ working on this recursive function, try to create a more (for you) normal function that does the countdown using a loop, and see what happens if you decorate that function with you slowdown() function.

Ex4: Decorating Game Characters

Solution

Background In the world of computer games, every character has a unique skill or ability that makes them special. For example, a character might have the ability to shoot accurately, move stealthily, or hack into computers.

We’re going to use Python decorators to add unique skills or abilities to game characters.

Task Create a Python decorator that adds a unique skill or ability to a game character. The decorator should be reusable, so that we can add multiple skills or abilities to a character.

Example Here’s an example of how the decorator might be used:

1@sharpshooter
2@stealthy
3def player():
4    return "I'm the player character"
5
6print(player())

The output of the code should be:

I'm the player character, the sharpshooter and stealthy character.

Steps

  1. Create a decorator function that takes a function as an argument and returns a new function that adds a unique skill or ability to the character’s description.

  2. Add the decorator to the player() function to add the “sharpshooter” and “stealthy” abilities to the player character.

  3. Test your code to make sure it works as expected.

Bonus

  1. Create additional decorators for other skills or abilities that might be found in a computer game.

  2. Add multiple skills or abilities to a single character by stacking multiple decorators.

Ex5: Menu register

Solution

In this exercise you should create a register.

When a new function is made you should by decorating it add it to a register (e.g a dictionary, or a list).

This functionality would be something that could be used in web applikation frameworks like Django or Flask. When ever a new function (a route or a page) is created and decorated this register could be used for a meny or many things like this.

Example:

1@register
2def home():
3     return 'I´m the home page'

You can get inspiration for this ecxercise in this document: Primer on Python Decorators