A Python module that provides transparent method profiling for any object through a wrapper class.
- Transparent Wrapping: Wrap any object and use it normally while automatically collecting timing data
- Method Timing: Automatically times every method call with high precision using
time.perf_counter() - Statistical Analysis: Built-in statistics including mean, median, std dev, min, max, and total execution time
- Class-level Storage: Timing data stored in a static dictionary accessible across all wrapper instances
- NumPy Integration: Returns timing data as NumPy arrays for easy analysis
Requires Python 3.6+ and NumPy:
pip install numpyfrom profiling_wrapper import ProfilingWrapper
# Your class
class Calculator:
def add(self, a, b):
return a + b
def multiply(self, a, b):
return a * b
# Wrap the object
calc = ProfilingWrapper(Calculator())
# Use it normally
calc.add(5, 10)
calc.add(3, 7)
calc.multiply(4, 6)
# Get profiling data
add_times = ProfilingWrapper.get_profiling_data("Calculator.add")
print(f"add() was called {len(add_times)} times")
print(f"Execution times: {add_times}")
# Get statistics
stats = ProfilingWrapper.get_statistics("Calculator.add")
print(f"Mean execution time: {stats['mean']:.6f}s")wrapper = ProfilingWrapper(obj)Creates a profiling wrapper around any object.
Parameters:
obj: Any object whose methods should be profiled
Retrieve timing data for a specific method.
Parameters:
key: Method identifier in format"{ClassName}.{method_name}"
Returns:
- NumPy array of execution times in seconds (length = number of calls)
Example:
times = ProfilingWrapper.get_profiling_data("Calculator.add")Get statistical summary for a method's execution times.
Parameters:
key: Method identifier in format"{ClassName}.{method_name}"
Returns:
- Dictionary with keys:
count,mean,median,std,min,max,total
Example:
stats = ProfilingWrapper.get_statistics("Calculator.add")
print(f"Average time: {stats['mean']:.6f}s")
print(f"Total calls: {stats['count']}")Retrieve timing data for all profiled methods.
Returns:
- Dictionary mapping method keys to NumPy arrays of execution times
Example:
all_data = ProfilingWrapper.get_all_profiling_data()
for method, times in all_data.items():
print(f"{method}: {len(times)} calls")Clear profiling data.
Parameters:
key: Optional method identifier. If provided, clears only that method's data. IfNone, clears all data.
Example:
# Clear all data
ProfilingWrapper.clear_profiling_data()
# Clear specific method data
ProfilingWrapper.clear_profiling_data("Calculator.add")from profiling_wrapper import ProfilingWrapper
class DataProcessor:
def process(self, data):
# Some processing
return len(data)
# Wrap and use
processor = ProfilingWrapper(DataProcessor())
processor.process([1, 2, 3])
processor.process([4, 5, 6, 7])
# Get results
times = ProfilingWrapper.get_profiling_data("DataProcessor.process")
print(f"Called {len(times)} times with average time {times.mean():.6f}s")# Create multiple wrapped instances
calc1 = ProfilingWrapper(Calculator())
calc2 = ProfilingWrapper(Calculator())
# All calls are tracked together
calc1.add(1, 2)
calc2.add(3, 4)
# Get combined data
times = ProfilingWrapper.get_profiling_data("Calculator.add")
print(f"Total calls across all instances: {len(times)}")import numpy as np
# Run some operations
obj = ProfilingWrapper(MyClass())
for i in range(100):
obj.expensive_operation()
# Analyze performance
stats = ProfilingWrapper.get_statistics("MyClass.expensive_operation")
print(f"Performance Analysis:")
print(f" Calls: {stats['count']}")
print(f" Average: {stats['mean']*1000:.2f}ms")
print(f" Median: {stats['median']*1000:.2f}ms")
print(f" Std Dev: {stats['std']*1000:.2f}ms")
print(f" Range: {stats['min']*1000:.2f}ms - {stats['max']*1000:.2f}ms")
print(f" Total Time: {stats['total']:.2f}s")# Profile multiple methods
obj = ProfilingWrapper(Calculator())
for i in range(50):
obj.add(i, i+1)
obj.multiply(i, 2)
# Compare performance
all_data = ProfilingWrapper.get_all_profiling_data()
for method, times in all_data.items():
avg_time = times.mean()
print(f"{method}: {avg_time*1000:.4f}ms average ({len(times)} calls)")- Wrapping: When you wrap an object,
ProfilingWrapperintercepts all attribute access via__getattr__ - Method Detection: When a callable method is accessed, it's wrapped with timing logic
- Timing: Each method call is timed using
time.perf_counter()(high-resolution timer) - Storage: Execution times are stored in a class-level dictionary with keys like
"ClassName.method_name" - Retrieval: Timing data can be retrieved at any time using class methods
- The wrapper is transparent - wrapped objects behave exactly like unwrapped ones
- Only methods are profiled; regular attributes are passed through unchanged
- Profiling data persists across wrapper instances (class-level storage)
- Use
clear_profiling_data()to reset between test runs - Timing precision depends on the system's
time.perf_counter()implementation
The wrapper adds minimal overhead to each method call:
- Attribute lookup
- Function wrapping
- Two
time.perf_counter()calls - One dictionary append operation
For most use cases, this overhead is negligible (typically < 1 microsecond).
Apache License