# Practical Decorators — Python

Source: https://www.geekswithgeeks.com/en/python/py-decorators-practical

> Common real-world uses: timing, logging, and decorators that take arguments.

## Timing a function

A timing decorator measures how long a function takes — useful for spotting slow code without touching its body.

```python
import time

def timed(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f}s")
        return result
    return wrapper

@timed
def slow_add(a, b):
    time.sleep(0.1)
    return a + b

slow_add(2, 3)
```

## Decorators with arguments

To make a decorator take its **own** arguments (like `@retry(times=3)`), wrap it in one more outer function layer.
