In Python, the @ symbol can be used to apply a decorator to a function. This is known as the "decorator @-notation". Here's an example:

def my_decorator(func):
    def new_func(*args, **kwargs):
        # Do something before calling the original function
        result = func(*args, **kwargs)
        # Do something after calling the original function
        return result
    return new_func
 
@my_decorator
def my_function():
    pass

In this example, we define a decorator my_decorator that wraps the original function with some additional functionality. We then apply the decorator to my_function using the @ symbol, which replaces the original function with the decorated function. The decorated function is created by calling the decorator with the original function as its argument.

The @ notation can also be used with decorators that take arguments. Here's an example:

def my_decorator(arg):
    def wrapper(func):
        def new_func(*args, **kwargs):
            # Do something with arg before calling the original function
            result = func(*args, **kwargs)
            # Do something after calling the original function
            return result
        return new_func
    return wrapper
 
@my_decorator(arg='hello')
def my_function():
    pass

In this example, we define a decorator my_decorator that takes an argument arg. We then define an inner function wrapper that takes the original function as its argument and returns a new function new_func that wraps the original function with some additional functionality.

We then apply the decorator to my_function using the @ symbol and passing the argument arg='hello'. This creates a new decorator function that takes the original function as its argument and returns the decorated function.

Using the @ notation to apply decorators can make code more concise and readable, especially when multiple decorators are applied to a single function. It also makes it clear that the function is being decorated, which can be helpful for understanding code.