Function Timing Decorator

A simple decorator to measure and print the execution time of any function. Useful for quick performance profiling during development.

Use Case

Use this when you want to quickly measure how long a function takes to execute without setting up complex profiling tools. Perfect for identifying slow functions during development.

Code

 1import time
 2from functools import wraps
 3
 4def timing_decorator(func):
 5    """Decorator that prints the execution time of a function."""
 6    @wraps(func)
 7    def wrapper(*args, **kwargs):
 8        start_time = time.perf_counter()
 9        result = func(*args, **kwargs)
10        end_time = time.perf_counter()
11        elapsed_time = end_time - start_time
12        print(f"{func.__name__} took {elapsed_time:.4f} seconds")
13        return result
14    return wrapper

Explanation

The decorator uses time.perf_counter() which provides the highest resolution timer available on the system. It wraps the original function, measures the time before and after execution, and prints the elapsed time.

The @wraps(func) decorator preserves the original function's metadata (name, docstring, etc.).

Parameters/Options

  • func: The function to be timed
  • Returns: The decorated function that includes timing

Examples

Example 1: Basic usage

1@timing_decorator
2def slow_function():
3    time.sleep(2)
4    return "Done"
5
6result = slow_function()

Output:

1slow_function took 2.0012 seconds

Example 2: With function arguments

1@timing_decorator
2def process_data(data, multiplier=2):
3    result = [x * multiplier for x in data]
4    return result
5
6output = process_data([1, 2, 3, 4, 5], multiplier=3)

Output:

1process_data took 0.0001 seconds

Example 3: Enhanced version with return value

 1import time
 2from functools import wraps
 3
 4def timing_decorator_v2(func):
 5    """Enhanced decorator that returns timing info."""
 6    @wraps(func)
 7    def wrapper(*args, **kwargs):
 8        start_time = time.perf_counter()
 9        result = func(*args, **kwargs)
10        end_time = time.perf_counter()
11        elapsed_time = end_time - start_time
12        
13        # Return both result and timing
14        return {
15            'result': result,
16            'elapsed_time': elapsed_time,
17            'function_name': func.__name__
18        }
19    return wrapper
20
21@timing_decorator_v2
22def calculate(n):
23    return sum(range(n))
24
25output = calculate(1000000)
26print(f"Result: {output['result']}")
27print(f"Time: {output['elapsed_time']:.4f}s")

Notes

For more detailed profiling, consider using:

  • cProfile module for comprehensive profiling
  • line_profiler for line-by-line profiling
  • memory_profiler for memory usage profiling

This decorator is best for quick checks during development, not for production use.

Gotchas/Warnings

  • ⚠️ Overhead: The decorator itself adds minimal overhead (~microseconds)
  • ⚠️ Nested decorators: Order matters when stacking multiple decorators
  • ⚠️ Async functions: This won't work with async functions - use asyncio timing instead
  • ⚠️ Production use: Remove or disable timing decorators in production for performance
comments powered by Disqus