-
Notifications
You must be signed in to change notification settings - Fork 0
.decorate()
Available with Timer and StaticTimer; allowing functions to be decorated such that when the wrapped function is called it will be timed. Timer.decorate() will store the measured times, StaticTimer.decorate() will either output the measured times or return them, depending on whether display=True or not. Both instances can call the wrapped function more than once by adjusting runs. The time will be measured runs number of times, but the function can actually be called multiple times per run by adjusting iterations_per_run.
Important: If a recursive function is being decorated, then the recursive call actually needs to be <func_name>.__wrapped__() to prevent the timing code from being executed again on the recursive call.
Note: .time_it() can provide similar functionality a .decorate(), but there are some cases where .decorate() is preferable.
- You want to time functions as they are called throughout the normal operation of your program. For example, you have a function interacting with a database and want to know the runtime of that function every time it gets called.
Note: .decorate() can take an argument called copiers which is either a function or a map of argument indices/names to functions. These functions are used to copy the argument before it is passed into the wrapped function on each iteration. This can be helpful, for example, if your function has side-effects and modifies the parameters. A prime example would be an in-place sorting function. If the list isn't copied, then subsequent iterations will be passed an already sorted list which could skew the results. In that case, copiers=list could be used to ensure that the wrapped function is getting a copy and not the original.
@StaticTimer.decorate(log_arguments=True)
def fib(n):
if n <= 2:
return 1
else:
return fib.__wrapped__(n-1) + fib.__wrapped__(n-2)
print(fib(5))
# 0.00352 ms - fib(5) [runs= 1, iterations= 1]
# 5log_arguments=True ensures that any arguments passed to the wrapped function will either be included in any output, if it is the StaticTimer, or logged with the run created by Timer so they can be used later for graphing, output, or best-fit-curve determination.
Lets change the decorator to this:
@StaticTimer.decorate(call_callable_args=True, log_arguments=True)print(fib(5))
# same two lines as above
print(fib(lambda: random.randint(5, 20)))
# 0.10938 ms - fib(14) [runs= 1, iterations= 1]
# 377call_callable_args=True doesn't change anything unless one of the parameters being passed to the wrapped function is callable. If it is, then the value passed to the function will actually be the result of calling the parameter, so func(callable_arg) becomes func(callable_arg()). Any callable arguments are replaced every run and keyword arguments are replaced as well.
If the timer is static, then runs are averaged together by default unless average_runs=False
# change the decorator to
@StaticTimer.decorate(runs=3, log_arguments=True, average_runs=False)
fib(10)
# 0.01791 ms - fib(10) [runs= 1, iterations= 1] | Run 1
# 0.01599 ms - fib(10) [runs= 1, iterations= 1] | Run 2
# 0.01535 ms - fib(10) [runs= 1, iterations= 1] | Run 3
# if average_runs=True (which is does by default)
fib(10)
# 0.01684 ms - fib(10) [runs= 3, iterations= 1]