Decorators are a powerful and flexible feature in python way to modify or enhance functions or methods without changing their existing code, they allow you to wrap another function, adding functionality before or after the wrapped function runs.
Define Decorator
A decorator is a function that takes another function as argument and returns a new function. This new function can add behaviour to the original function.
Syntax for DecoratorsTypically, decorators use the @decorator_name placed above or below of the function definition as shown in the below example.
Example 1: Here is the basic example which illustrates working of Decorators.
This decorator will print a message before and after the execution of a function.
def log_decorator(func): def wrapper (*args, **kwargs): print (f"Calling function: {func.__name__}") result = func(*args, **kwargs) # Call the original function print (f"Function {func.__name__} finished") return result return wrapper @log_decorator def greet(name): return f"Hello, {name}!" # Usage print(greet("Madhu"))
Here what the decorator does
1. log_decorator is defined to take a function func as an argument.Inside, it defines a wrapper function that adds logging behavior.
2. Logging: The wrapper function prints a message before calling the original function and another message after it finishes.
3. Applying the Decorator: The @log_decorator syntax applies the decorator to the greet function. When you call greet("Madhu"), it actually calls wrapper.
OutputWhen you run the code, you’ll see:
Calling function: greet
function: greet finished
Hello, Madhu!
Simplicity : This example illustrates how decorators can add functionality (logging) in a clear and simple way.
Functionality Separation: The logging is separated from the core logic of the greet function, making the code cleaner and easier to maintain.
This example shows the basic mechanism of decorators in a straightforward manner!
Example 2: Authorization Decorator
This decorator will simulate user authorization by checking if a user has the right permissions to execute a function.
def requires_authentication(func): def wrapper(user, *args, **kwargs): if not user.get('is_authenticated', False): raise PermissionError("User is not authorized to perform this action.") return func(user, *args, **kwargs) # Call the original function return wrapper @requires_authentication def access_secure_resource(user): return "Access granted to secure resource!" # Usage example user1 = {'name': 'Alice', 'is_authenticated': True} user2 = {'name': 'Bob', 'is_authenticated': False} # Authorized user try: print(access_secure_resource(user1)) # Output: Access granted to secure resource! except PermissionError as e: print(e) # Unauthorized user try: print(access_secure_resource(user2)) # Raises PermissionError except PermissionError as e: print(e) # Output: User is not authorized to perform this action.Explanation
1. Decorator Definition:
The requires_authentication decorator takes a function func as an argument. Inside, it defines a wrapper function that checks the user object for an is_authenticated attribute.
2. Authorization Check:If the user is not authenticated, a PermissionError is raised. If the user is authenticated, the original function func is called with the user and any additional arguments.
3. Applying the Decorator:The @requires_authentication syntax applies the decorator to access_secure_resource. When access_secure_resource(user) is called, it first goes through the wrapper function.
Flexibility: You can customize the authorization logic based on your application needs.
Error Handling: The decorator can raise exceptions, which can be caught and handled in the calling code.
Reusable: This decorator can be reused with any function that requires user authentication.
This example showcases how decorators can encapsulate cross-cutting concerns like authentication, keeping your code clean and focused on its core functionality.