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.
Related
I'm trying to create wrapper. My code works with simple example function, but when I apply it to example function, it stops working.
Code of my wrapper:
import functools
import time
from functools import wraps
def profiler(func):
#wraps(func)
def wrapper(*args, **kwds):
setattr(func, 'calls', 0)
t1 = time.monotonic() # what's time at the beginning of run
func(*args, **kwds)
func.calls += 1 # add 1 to number of calls
t2 = time.monotonic() # what's time at the beginning of run
last_time_taken = t2 - t1
setattr(wrapper, 'last_time_taken', last_time_taken)
setattr(wrapper, 'calls', func.calls)
return wrapper
I would be glad to get any advice helping to solve this error. Thanx!
Your wrapper doesn't return, which is the same as returning None. When you call the decorated function, you will in practice just run the wrapper.
Make sure the wrapper returns the result of your function and it will work just fine.
import functools
import time
from functools import wraps
def profiler(func):
#wraps(func)
def wrapper(*args, **kwds):
setattr(func, 'calls', 0)
t1 = time.monotonic() # what's time at the beginning of run
res = func(*args, **kwds)
func.calls += 1 # add 1 to number of calls
t2 = time.monotonic() # what's time at the beginning of run
last_time_taken = t2 - t1
setattr(wrapper, 'last_time_taken', last_time_taken)
setattr(wrapper, 'calls', func.calls)
return res
return wrapper
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
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
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
It was something like cMessage I think? I can't remember, could someone help me?
cProfile ?
To time a function, you can also use a decorator like this one:
from functools import wraps
import time
def timed(f):
"""Time a function."""
#wraps(f)
def wrapper(*args, **kwds):
start = time.clock()
result = f(*args)
end = 1000 * (time.clock() - start)
print '%s: %.3f ms' % (f.func_name, end)
return result
return wrapper
And "mark" your fonction by "#timed" like that:
#timed
def toBeTimed():
pass