This repository contains supporting material for the Introduction to Decorators - Power Up Your Python Code tutorial I presented at the Pycon Online 2021 conference.
The tutorial is available as a video on YouTube. Thanks to PyCon for allowing me to present, and thanks to Ben and Diane for hosting and for captioning the video.
The tutorial is centered around several exercises asking you to implement different decorators, and reinforcing concepts introduced during the tutorial. Please see the video for full context. The exercises are available as PDF-slides, and also listed below. Solutions to the exercises are available in the Code-section.
Write a decorator that prints BEFORE
before calling the decorated function and AFTER
afterwards.
>>> @before_and_after
... def greet(name):
... print(f"Hi {name}!")
...
>>> greet("PyCon")
BEFORE
Hi PyCon!
AFTER
Write a decorator that runs the decorated function twice and returns a 2-tuple with both return values.
>>> import random
>>> @do_twice
... def roll_dice():
... return random.randint(1, 6)
...
>>> roll_dice()
(3, 1)
Write a decorator that stores references to decorated functions in a dictionary.
>>> FUNCTIONS = {}
>>> @register
... def roll_dice():
... return random.randint(1, 6)
...
>>> FUNCTIONS
{'roll_dice': <function __main__.roll_dice()>}
>>> FUNCTIONS["roll_dice"]()
2
Write a decorator that repeatedly calls the decorated function as long as it raises an exception.
>>> @retry
... def only_roll_sixes():
... number = random.randint(1, 6)
... if number != 6:
... raise ValueError(number)
... return number
...
>>> only_roll_sixes()
6
Rewrite @retry
so that it only retries on specific, user-defined exceptions.
>>> @retry(ValueError)
... def calculation():
... number = random.randint(-5, 5)
... if abs(1 / number) > 0.2:
... raise ValueError(number)
... return number
...
>>> calculation()
# -5, 5, or ZeroDivisionError
Adapt @retry
so that it only tries a maximum of max_retries
times.
>>> @retry(max_retries=3)
... def only_roll_sixes():
... number = random.randint(1, 6)
... if number != 6:
... raise ValueError(number)
... return number
...
>>> only_roll_sixes()
# 6 or ValueError
Challenge: Can you make @retry
count all retries across several function calls?
Write a class-based @Retry
decorator that keeps track of the number of retries across all function calls.
>>> @Retry
... def only_roll_sixes():
... number = random.randint(1, 6)
... if number != 6:
... raise ValueError(number)
... return number
...
>>> only_roll_sixes()
Retry attempt 1
Retry attempt 2
6
>>> only_roll_sixes()
Retry attempt 3
6
All code was written live during the tutorial. You can download two files containing the code:
console.py
: A slightly cleaned up version of the code written in the console during the tutorialdecorators.py
: Functions and decorators copied over to the editor during the tutorial
Both files contain essentially the same code, including solutions to all exercises. The console.py
file contains a bit more of the code used to run the examples as well.
-
Real Python articles:
-
Real Python video course: Python Decorators 101
-
Python Enhancement Proposals about decorators:
-
Other resources: