Python” : “Decorators for Timing”, Retrying, Caching and Validating

Python” : “Decorators for Timing”, Retrying, Caching and Validating
3 min read

Python decorators allow adding common behaviors to a function, such as timing, retrying, caching, or argument validation, without modifying its internal code. In a tutorial published on Machine Learning Mastery, the author details how they work and shows how to build reliable ones, without hiding exceptions.

The starting point is a data processing module composed of three functions which, over the weeks, have taken on ancillary tasks. According to the tutorial, one function checks that its path exists before loading embeddings, another repeats its requests in a loop with time.sleep(), and the third measures its execution time and stores its results in a dictionary. This boundary code, added manually, varies from one function to another, and each new function risks introducing a variant.

A decorator, a simple assignment

A decorator is a function that takes a function as an argument and returns a wrapper, i.e., an envelope that executes code before and after the original call. The tutorial reminds us that the @ syntax is equivalent to reassigning the function’s name, what the Python glossary calls syntactic sugar. The decorator is therefore executed only once, at the time the function is defined.

The wrapper retains the original function thanks to a closure, a mechanism by which an inner function retains access to the variables of the function that encloses it. The parameters *args and **kwargs pass the arguments, and the result is returned as is. Without further precaution, however, the wrapper replaces the function’s name and documentation. The tutorial therefore recommends using functools.wraps, which copies these metadata, as well as the __wrapped__ attribute, which allows access to the undecorated function during testing.

Four boundary behaviors

The timing decorator relies on time.perf_counter(), a clock used to calculate durations, not dates. Placed in a try/finally block, it displays the duration even when the function fails, the exception remaining transmitted to the caller.

The retry decorator is a factory, which accepts options such as max_attempts, delay and the exceptions to retry. It only retries configured exceptions, so that a programming error is not treated as a network problem. On the last attempt, a bare raise re-raises the original exception. The tutorial notes a limitation, however: this approach is only safe for calls that can be executed twice without consequence. A payment or a message send does not meet this criterion.

For caching, the tutorial recommends functools.lru_cache, which stores the results of the most recent calls, up to a limit of maxsize (for example 256). Its constraints stem from its implementation: arguments must be hashable, and a function returning a mutable object can produce unexpected effects. Finally, validation uses inspect.signature() to associate arguments with parameter names, even when they are passed by position or with their default value, then applies simple rules before the call.

The tutorial concludes with a cautionary rule: a decorator should not decide the business logic of the function. When the behavior depends on the data, it is better to place it in the function, or in a data model for validation. When multiple decorators are stacked, their order of application matters, as the highest one is the outermost wrapper.

S’abonner
Notification pour
guest

0 Commentaires
Le plus ancien
Le plus récent Le plus populaire
0
Nous aimerions avoir votre avis, veuillez laisser un commentaire.x