Function for calculation execution time of other functions - python

I want to create a function that calculates the execution time of other functions, but when I do that, I get an error like: 'int' object is not callable. What is the problem here?
import time
def square(x):
return x**2
def timer(func):
t1 = time.perf_counter()
func()
t2 = time.perf_counter()
print(t2-t1)
timer(square(5))

It is also possible to modify your code to make it work, but you'll have to pass in the arguments of square() into timer() after passing in the function as the first argument:
def timer(func, *args, **kwargs):
t1 = time.perf_counter()
func(*args, **kwargs)
t2 = time.perf_counter()
print(t2-t1)
timer(square, 5)
Using *args* and **kwargs lets us deal with functions with arbitrary parameters.
A more convenient way to do this is to use a decorator. It returns a wrapper function around the original function. You don't have to change much in order to time a particular function. Here's an example:
def timer(func):
def wrapper(*args, **kwargs):
func_name = func.__name__
print(f"Starting {func_name}")
t1 = time.perf_counter()
output = func(*args, **kwargs)
t2 = time.perf_counter()
print(f"Total time for {func_name}: {t2 - t1:.3f} s\n")
return output
return wrapper
To use it, simply do:
#timer
def square(x):
return x**2
square(5)
Or:
def square(x):
return x**2
timed_square = timer(square)
timed_square(5)

Your timer expects a function to call, but you're giving it the result of already calling one (and that result isn't a function).
You can do timer(lambda: square(5)) instead. Then it's your timer function that executes the (anonymous) function and thus the expression square(5) as intended.

square(5) return 25; so you're trying to run your timer on a number ;)
Try instead:
import time
def timer(func):
def f(args):
t1 = time.perf_counter()
ret = func(args)
t2 = time.perf_counter()
print('execution time: {}'.format(t2-t1))
return ret
return f
def square(x):
return x**2
timedSqure = timer(square)
res = timedSqure(5)
print(res)
Further, I recommend on learning decorators in python, because with decorators you can make it even more elegant by declaring:
#timer
def square(x):
return x**2
See repl here
And last, per #Heap Overflow's comment: it doesn't make sense to time something that runs so quickly. If you want to benchmark a function you should use timeit

Related

How to decorate a property to measure the time it executes?

I tried to write a decorator to compute the time of the computation for methods in a class, but I also have a lot of properties.
I tried to write a decorator for a property as follows:
def timer(method):
import time
#property
def wrapper(*args, **kw):
start = time.time()
result = method
end = time.time()
print('Elapsed time for: {} is: {}s'.format(method.__name__,(end-start)*1000))
return result
return wrapper
I can't get the name of the property as written, moreover perhaps you would suggest to write it in another way?
You would need to stack decorators:
def timer(method):
import time
def wrapper(*args, **kwargs):
start = time.time()
result = method(*args, **kwargs) # note the function call!
end = time.time()
print('Elapsed time for: {} is: {}s'.format(method.__name__,(end-start)*1000))
return result
return wrapper
class X:
#property
#timer
def some_method(self):
# timed code
return 0
>>> x = X()
>>> x.some_method
Elapsed time for: some_method is: 0.0050067901611328125s
0

Python - ensuring decorated function keeps metadata

Could you please let me know if there is a way for a decorated function to keep its metadata?
This would be the code for the decorator:
def timer(func):
"""prints how long a function takes to run."""
def wrapper(*args, **kwargs):
t_start = time.time()
result = functionalists(*args, **kwargs)
t_total = time.time() - t_start
print('{} took {}s'.format(functionalists.__name__, t_total))
return result
return wrapper
The following would be the decorated function.
#timer
def sleep_n_seconds(n=10):
"""pause processing for n seconds.
Args:
n (int): The number of seconds to pause for.
"""
time.sleep(n)
When I try to print the docstrings with the following code, the metadata is not returned.
print(sleep_n_seconds.__doc__)
Please let me know if I need to provide further details.
Thank you
Use the wraps function from functools module to retain the signature. :
from functools import wraps
def timer(func):
#wraps(func)
"""prints how long a function takes to run."""
def wrapper(*args, **kwargs):
t_start = time.time()
result = functionalists(*args, **kwargs)
t_total = time.time() - t_start
print('{} took {}s'.format(functionalists.__name__, t_total))
return result
return wrapper

Decorated function returns "None"

I'm extremely new to python, and i just encountered decorators. I'm still kinda confused by them but i am learning
i was trying to make a decorator that tells me how much time my function took to finish, but apparently when i try to use it on a function that should return something, it just returns "None"
I've seen only a couple of questions talking about this problem but none of them actually helped
Here's my code
import time
def time_it(func): # Here i make a simple decorator function that should time my decorated function
def wrapper(*args, **kwargs):
t1 = time.time()
func(*args)
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
return wrapper
#time_it
def square(nums): # I make a function that squares every number in a list
new_list = []
for n in nums:
new_list.append(n ** 2)
return new_list
lis = [f for f in range(200000)] # i make a list with a range of 200000
print(square(lis))
sorry for any grammatical errors, i'm not a native english speaker
The problem is that your inner function return value isn't being returned. The change is noted below:
from functools import wraps
def time_it(func): # Here i make a simple decorator function that should time my decorated function
#wraps(func)
def wrapper(*args, **kwargs):
t1 = time.time()
## Note the change on this line -- I now store the return result from the called function
result = func(*args, **kwargs)
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
## And then explicitly return the result
return result
return wrapper
For the decorator, you need to remember that it's just a closure, with some fancy syntax. You still need to deal with the function return parameters yourself.
A couple of additions:
from functools import wraps and #wraps(func)
this will create wrap the inner function with some details that exist in the wrapping function. There's a small example in the python docs here:
https://docs.python.org/3/library/functools.html
The decorator replaces square with wrapper and wrapper does not return anything. It should return the value returned by the wrapped function.
This is the correct way to do it:
def time_it(func):
def wrapper(*args, **kwargs):
t1 = time.time()
try:
return func(*args, **kwargs)
finally:
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
return wrapper
I changed 3 things:
added return, so that the value is returned from decorated function
added **kwargs to func calls, because it may be needed if used differently
added try/finally block, so that the printout happens even in case of an exception, plus this makes it easier to return the value.
Your decorated function doesn't return anything explicitely - so, by default, it returns None.
You can capture the output before printing the time, and return at the end:
def time_it(func): # Here i make a simple decorator function that should time my decorated function
def wrapper(*args, **kwargs):
t1 = time.time()
out = func(*args)
t2 = time.time()
total = t2 - t1
print("The function '" + func.__name__ + "' took", str(total)[0:5], "seconds to complete")
return out
return wrapper

How to call one function with parameter in other function by Python?

I know the title is a little bit confusing.
So let me take this code as example:
import timeit
def test1(x,y):
return x*y
def time_cost(func):
start = timeit.default_timer()
func()
stop = timeit.default_timer()
print(stop - start)
time_cost(test1)
I want to give test1's two parameter, x and y, to time_cost function.
But I don't know how to do so.
I have tried this before:
import timeit
def test1(x,y):
return x*y
def time_cost(func):
start = timeit.default_timer()
func
stop = timeit.default_timer()
print(stop - start)
time_cost(test1(1,2))
It can run but the result is so weird that I believe this is wrong.
So how to do this? Thank you all.
Return a function that does what you want.
def time_cost(func):
def run(*args, **kwargs):
start = timeit.default_timer()
func(*args, **kwargs)
stop = timeit.default_timer()
print(stop - start)
return run
Then call it.
time_cost(test1)(1, 2)
Give this a try.
from functools import wraps
import timeit
def time_cost(func):
#wraps(func)
def wrapper(*args, **kwargs):
start = timeit.default_timer()
result = func(*args, **kwargs)
stop = timeit.default_timer()
print(stop - start)
return result
return wrapper
test1 = time_cost(test1)
test1(1,2)
This kind of function is called a decorator. Also called a wrapper, or a closure.
You can also use this with the decorator syntax:
#time_cost
def test1(x,y):
return x*y
test1(1,2)
This does the same as the code above.

Assure minimal wait between calls with decorators

I'm building a wrapper for an API which demands at least 1 second of waiting between each call. I thought I could solve this using a decorator in the following way:
import datetime, time
last_time = datetime.datetime(2014, 1, 1)
def interval_assurer(f):
global last_time
if (datetime.datetime.now() - last_time).seconds < 1:
print("Too fast...")
time.sleep(1)
last_time = datetime.datetime.now()
return f
#interval_assurer
def post():
pass
This won't work though, for some reason, and I'm not sure why. last_time gets updated the first time the post is called, but won't update afterwards. Please keep in mind that this is the first time I'm experimenting with decorators, so I am probably missing something fundamental.
Thanks.
Since your interval_assurer is a decorator, it's called exactly once: when the function is defined, and not when it's called. You need to create a wrapping function like this:
import time, functools
def interval_assurer(f):
last_time = [0]
#functools.wraps(f) # optional, but nice to have
def wrapper(*args, **kwargs):
time_diff = time.time() - last_time[0]
if time_diff < 1:
print("Too fast...")
time.sleep(1 - time_diff)
last_time[0] = time.time()
return f(*args, **kwargs)
return wrapper
#interval_assurer
def post(self, **kwargs):
pass
You also won't need the global then (the trick with the list can be replaced with nonlocal in Python 3).
The decorator function just returns the original function, so the timing code only runs when the decorator is called, i.e. when the function definition is evaluated. Instead, it should return a new function that incorporates the timing code and calls f when appropriate.
Try something like:
def interval_assurer(f):
def func():
global last_time
if (datetime.datetime.now() - last_time).seconds < 1:
print("Too fast...")
time.sleep(1)
last_time = datetime.datetime.now()
return f()
return func
If your decorated function takes arguments, you should include *args, **kwargs in the definition of func and the call to f; also, consider decorating func in turn with functools.wraps(f).
Building on #bereal's answer, you can make the last_time an attribute of the wrapper function to remove the global (allowing multiple wrapped functions, each with their own timer), and even make a decorator that takes an argument for the interval to enforce:
import functools
import time
def interval_assured(interval):
"""Ensure consecutive calls are separated by a minimal interval."""
def wrapper(f):
#functools.wraps(f)
def func(*args, **kwargs):
if (time.time() - func.last_time) < interval:
time.sleep(interval)
result = f(*args, **kwargs)
func.last_time = time.time()
return result
func.last_time = time.time()
return func
return wrapper
Note that the time is reset after the wrapped function f is called - this is important if the run-time of f is large relative to the interval.
In use:
>>> def testing(x):
print time.time()
print x
>>> for x in range(3):
testing(x)
1405938405.97
0
1405938406.01
1
1405938406.02
2
>>> #interval_assured(5)
def testing(x):
print time.time()
print x
>>> for x in range(3):
testing(x)
1405938429.71
0
1405938434.73
1
1405938439.75
2

Categories