Les fermetures et les décorateurs sont des fonctionnalités puissantes de Python qui vous permettent d'écrire du code plus flexible et réutilisable. Comprendre ces concepts fera passer vos compétences Python au niveau supérieur, vous permettant de gérer facilement des scénarios plus complexes tels que la journalisation, le contrôle d'accès et la mémorisation.
Dans cet article de blog, nous explorerons :
À la fin de cet article, vous aurez une solide maîtrise des fermetures et des décorateurs, et vous serez en mesure de les appliquer efficacement dans votre propre code.
En Python, les fermetures sont des fonctions qui conservent les valeurs des variables de leur portée lexicale englobante même lorsque la fonction externe a fini de s'exécuter. Les fermetures sont un moyen de conserver l'état entre les appels de fonction, ce qui les rend utiles dans les scénarios dans lesquels vous devez conserver un certain contexte.
Les fermetures se composent de trois éléments principaux :
Voici un exemple de fermeture simple :
def outer_function(message): def inner_function(): print(message) return inner_function # Create a closure closure = outer_function("Hello, World!") closure() # Output: Hello, World!
Dans cet exemple, inner_function fait référence à la variable de message de external_function, même après la fin de l'exécution de external_function. La fonction interne "ferme" la variable de la portée externe, d'où le terme fermeture.
Les fermetures fonctionnent en capturant l'état des variables libres et en les stockant dans l'attribut __closure__ de l'objet fonction.
Inspectons la fermeture de l'exemple précédent :
print(closure.__closure__[0].cell_contents) # Output: Hello, World!
L'attribut __closure__ contient les références aux variables que la fermeture conserve. Chaque variable est stockée dans une « cellule » et vous pouvez accéder à son contenu avec cell_contents.
Les fermetures sont particulièrement utiles lorsque vous souhaitez conserver l'état entre les appels de fonction sans utiliser de variables ou de classes globales. Voici quelques cas d'utilisation courants :
Vous pouvez utiliser des fermetures pour créer des fonctions de manière dynamique.
def multiplier(factor): def multiply_by_factor(number): return number * factor return multiply_by_factor times_two = multiplier(2) times_three = multiplier(3) print(times_two(5)) # Output: 10 print(times_three(5)) # Output: 15
Dans cet exemple, multiplicateur renvoie une fonction qui multiplie un nombre donné par un facteur spécifique. Les fermetures times_two et times_trois conservent la valeur du facteur de leur portée englobante.
Les fermetures vous permettent d'encapsuler un comportement sans exposer l'état interne. Ceci est similaire au concept de méthodes privées dans la programmation orientée objet.
def counter(): count = 0 def increment(): nonlocal count count += 1 return count return increment counter_fn = counter() print(counter_fn()) # Output: 1 print(counter_fn()) # Output: 2
Dans cet exemple, la variable count est encapsulée dans la fermeture, et seule la fonction d'incrémentation peut modifier sa valeur.
Un décorateur est une fonction qui prend une autre fonction et étend ou modifie son comportement sans modifier le code de la fonction d'origine. Les décorateurs sont souvent utilisés pour ajouter des fonctionnalités telles que la journalisation, le contrôle d'accès ou la synchronisation aux fonctions et méthodes.
En Python, les décorateurs sont appliqués aux fonctions en utilisant le symbole @ au-dessus de la définition de la fonction.
def decorator_function(original_function): def wrapper_function(): print(f"Wrapper executed before {original_function.__name__}()") return original_function() return wrapper_function @decorator_function def say_hello(): print("Hello!") say_hello() # Output: # Wrapper executed before say_hello() # Hello!
Ici, decorator_function est appliqué à say_hello, ajoutant des fonctionnalités supplémentaires avant l'exécution de say_hello().
Les décorateurs sont essentiellement du sucre syntaxique pour un modèle courant en Python : des fonctions d'ordre supérieur, qui prennent d'autres fonctions comme arguments. Quand vous écrivez @decorator, cela équivaut à :
say_hello = decorator_function(say_hello)
La fonction décorateur renvoie une nouvelle fonction (wrapper_function), qui étend le comportement de la fonction d'origine.
Si la fonction en cours de décoration prend des arguments, la fonction wrapper doit accepter *args et **kwargs pour transmettre les arguments.
def decorator_function(original_function): def wrapper_function(*args, **kwargs): print(f"Wrapper executed before {original_function.__name__}()") return original_function(*args, **kwargs) return wrapper_function @decorator_function def display_info(name, age): print(f"display_info ran with arguments ({name}, {age})") display_info("John", 25) # Output: # Wrapper executed before display_info() # display_info ran with arguments (John, 25)
Python fournit plusieurs décorateurs intégrés, tels que @staticmethod, @classmethod et @property.
These decorators are commonly used in object-oriented programming to define methods that are either not bound to the instance (@staticmethod) or bound to the class itself (@classmethod).
class MyClass: @staticmethod def static_method(): print("Static method called") @classmethod def class_method(cls): print(f"Class method called from {cls}") MyClass.static_method() # Output: Static method called MyClass.class_method() # Output: Class method called from <class '__main__.MyClass'>
The @property decorator allows you to define a method that can be accessed like an attribute.
class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value <= 0: raise ValueError("Radius must be positive") self._radius = value c = Circle(5) print(c.radius) # Output: 5 c.radius = 10 print(c.radius) # Output: 10
You can write your own decorators to add custom functionality to your functions or methods. Decorators can be stacked, meaning you can apply multiple decorators to a single function.
Here’s a custom decorator that measures the execution time of a function:
import time def timer_decorator(func): def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(f"{func.__name__} ran in {end_time - start_time:.4f} seconds") return result return wrapper @timer_decorator def calculate_square(numbers): result = [n * n for n in numbers] return result nums = range(1, 1000000) calculate_square(nums)
Decorators can also accept their own arguments. This is useful when you need to pass configuration values to the decorator.
def logger_decorator(message): def decorator(func): def wrapper(*args, **kwargs): print(f"{message}: Executing {func.__name__}") return func(*args, **kwargs) return wrapper return decorator @logger_decorator("DEBUG") def greet(name): print(f"Hello, {name}!") greet("Alice") # Output: # DEBUG: Executing greet # Hello, Alice!
In this example, the decorator logger_decorator takes a message as an argument, and then it wraps the greet function with additional logging functionality.
Decorators can be applied not only to functions but also to classes. Class decorators modify or extend the behavior of entire classes.
def add_str_repr(cls): cls.__str__ = lambda self: f"Instance of {cls.__name__}" return cls @add_str_repr class Dog: pass dog = Dog() print(dog) # Output: Instance of Dog
Memoization is an optimization technique where the results of expensive function calls are cached, so subsequent calls with the same arguments can be returned faster.
def memoize(func): cache = {} def wrapper(*args): if args not in cache: cache[args] = func(*args) return cache[args] return wrapper @memoize def fibonacci(n): if n in [0, 1]: return n return fibonacci(n - 1) + fibonacci(n - 2) print(fibonacci(30)) # Output: 832040
Closures and decorators are advanced Python concepts that unlock powerful capabilities for writing cleaner, more efficient code. Closures allow you to maintain state and encapsulate data, while decorators let you modify or extend the behavior of functions and methods in a reusable way. Whether you're optimizing performance with memoization, implementing access control, or adding logging, decorators are an essential tool in your Python toolkit.
By mastering these concepts, you'll be able to write more concise and maintainable code and handle complex programming tasks with ease.
Feel free to experiment with closures and decorators in your projects and discover how they can make your code more elegant and powerful!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!