python-course.eu

3. Type Annotations Decorators

By Bernd Klein. Last modified: 13 Jul 2023.

This chapter of our Python course is about decorators but only about how to annotate decorators with type annotations or type hints. If you want to learn all about decorators, we highly recommend our tutorials on Python decorators:

You will learn in this chapter how to annotate decorator functions, allowing you to enhance code readability and maintainability. We provide explanations of the syntax and demonstrate the benefits of using type annotations in your decorators.

Two real decorators at work

Our first example will be a decorator for calling how often a function has been called.

You have to know a few things about the Jupyter-Notebooks cells (ipython shell) to understand how we save programs and start mypy: With the shell magic %%writefile example1.py we can write the content of a cell into a file with the name example1.py. In IPython syntax, the exclamation mark (!) allows users to run shell commands (from your operating system) from inside a Jupyter Notebook code cell. Simply start a line of code with ! and it will run the command in the shell. We use this to call mypy on the Python file.

%%writefile example.py

from math import sin, cos

from typing import Callable

def call_counter(func: Callable) -> Callable:
    def wrapper(*args, **kwargs):
        wrapper.counter += 1
        return func(*args, **kwargs)  
    setattr(wrapper, 'counter', 0)
    # not possible:
    setattr(wrapper, 'counter', 0)
    return wrapper

sin = call_counter(sin)
cos = call_counter(cos)

@call_counter
def add(a: int, b: int) -> int:
    return a + b

result = add(5, 3)
print(result)

print(sin(3.5))
print(sin(36.5))
print(getattr(sin, 'counter'))

OUTPUT:

Overwriting example.py

We can see that this Python code above works, if we let it run with Python:

!python example.py

OUTPUT:

8
-0.35078322768961984
-0.9317168878547055
2

We can also see that it works with mypy as well:

!mypy example.py

OUTPUT:

Success: no issues found in 1 source file

The Future: PEP-612 with ParamSpec and Concatenate

%%writefile example.py
from typing import Callable, Any, Dict

def memoize(f: Callable) -> Callable:
    memo: Dict[Any, Any] = {}
    def helper(x: Any) -> Any:
        if x not in memo:            
            memo[x] = f(x)
        return memo[x]
    return helper
    

def fib(n: int) -> int:
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n-1) + fib(n-2)

fib = memoize(fib)

fib(10)

OUTPUT:

Overwriting example.py
!mypy example.py

OUTPUT:

Success: no issues found in 1 source file

Live Python training

instructor-led training course

Enjoying this page? We offer live Python training courses covering the content of this site.

Upcoming online Courses

Python Intensive Course

10 Mar 2025 to 14 Mar 2025
07 Apr 2025 to 11 Apr 2025
23 Jun 2025 to 27 Jun 2025
28 Jul 2025 to 01 Aug 2025

Data Analysis with Python

12 Mar 2025 to 14 Mar 2025
09 Apr 2025 to 11 Apr 2025
04 Jun 2025 to 06 Jun 2025
30 Jul 2025 to 01 Aug 2025

Efficient Data Analysis with Pandas

10 Mar 2025 to 11 Mar 2025
07 Apr 2025 to 08 Apr 2025
02 Jun 2025 to 03 Jun 2025
23 Jun 2025 to 24 Jun 2025
28 Jul 2025 to 29 Jul 2025

Machine Learning from Data Preparation to Deep Learning

10 Mar 2025 to 14 Mar 2025
07 Apr 2025 to 11 Apr 2025
02 Jun 2025 to 06 Jun 2025
28 Jul 2025 to 01 Aug 2025

Overview of all Python training courses