I'm trying to understand the real world use of decorators.
coming on the decorators we all know that it is used to decorate the function.
It means we can add something extra to the existing function but it can be done by using other simple function also we can call one function which will call the other function. So why we need to use the decorators.
I have already tried to make two program
with decorators
def decor_result(result_as_argument):
def new_function(marks):
for i in marks:
if i>= 75:
print("Congrats distiction",i)
else:
result_as_argument(marks)
return new_function
#decor_result
def result(marks):
for i in marks:
if i >= 35:
pass
else:
print("Fail")
break
else:
print("Pass")
result([79,65,55,78,12])
without decorators
def result_distict(marks):
for i in marks:
if i>= 75:
print("Congrats distiction",i)
else:
result(marks)
def result(marks):
for i in marks:
if i >= 35:
pass
else:
print("Fail")
break
else:
print("Pass")
result_distict([79,65,55,78,12])
result([79,65,55,78,12])
By doing this I came to know that without using decorators, it is more simplified and we are free to use any of the function what we want and by using decorators we can not use the old function so why and where to use decorators?
In your example, doing a decorator is not necessary. You want to use a decorator when you're trying to implement a specific behavior to a set of functions. For instance, let's say you're trying to display the execution time of all the functions in your script.
Solution 1) You add a little piece of code everywhere to display it:
from time import time
def f1():
t0 = time()
# f1 body
print("Execution time of f1: {}".format(time()-t0))
def f2():
t0 = time()
# f2 body
print("Execution time of f2: {}".format(time()-t0))
As you can see, the code is very repetitive. If you want to change anything in this shared behavior, then you have to modify all the functions. That's where decorator are useful.
2) Using decorators:
def timer(func):
def wrapper(*args,**kwargs):
t0 = time()
res = func(*args,**kwargs)
duration = time()-t0
print("Execution time of {}: {} s".format(func.__name__, duration))
return res
return wrapper
#timer
def f1():
# body of f1
#timer
def f2():
# body of f2
Related
I have two functions, function1 and function2. I want to execute function1 for only 5 seconds, and then execute function2 for only 3 seconds and then repeat it.
I tried time.sleep() function, but it freezes entire program not executing any function.
I tried asyncio and threading, but it just executing both functions at same time.
def function1():
//do something
def funtion2():
//do something else
while True:
function1()
// execute function1 for 5 seconds
function2()
// execute function2 for 3 seconds
How precise do you need to be? Are you needing to account for the runtime of the functions themselves? That will be quite difficult.
Here's a simple approach:
import time # built-in module
def timed_execution(func, s, *args, **kwargs):
t0 = time.time()
while True:
func(*args, **kwargs)
if time.time() - t0 >= s:
break
timed_execution(function1, 5)
timed_execution(function2, 3)
If you'd like to get a little more fancy, and the times your functions need to execute is always the same, you could use a decorator:
import time
def timer(s):
def timed_func(func):
def timed_execution(*args, **kwargs):
t0 = time.time()
while True:
func(*args, **kwargs)
if time.time() - t0 >= s:
break
return timed_execution
return timed_func
#timer(5)
def function1():
pass
#timer(3)
def function2():
pass
If you want to use a decorator with a parameter for the amount of time, but only optionally, you'll need to do a bit more work. See how to do a conditional decorator.
I have a question about adding delay after calling various functions.
Let's say I've function like:
def my_func1():
print("Function 1")
def my_func2():
print("Function 2")
def my_func3():
print("Function 3")
Currently I've added delay between invoking them like below:
delay = 1
my_func1()
time.sleep(delay)
my_func2()
time.sleep(delay)
my_func3()
time.sleep(delay)
As you can see I needed a few times time.sleep, which I would like to avoid.
Using decorator is also not an option, since it might be that I would like to avoid delay when calling one of this function not in a group.
Do you have any tip how to beautify this?
You can define something like this:
def delay_it(delay, fn, *args, **kwargs):
return_value = fn(*args, **kwargs)
time.sleep(delay)
then
a = delay_it(1, my_func1, "arg1", arg2="arg2")
b = delay_it(1, my_func2, "arg3")
...
I've tested this based on "How to Make Decorators Optionally Turn On Or Off" (How to Make Decorators Optionally Turn On Or Off)
from time import sleep
def funcdelay(func):
def inner():
func()
print('inner')
sleep(1)
inner.nodelay = func
return inner
#funcdelay
def my_func1():
print("Function 1")
#funcdelay
def my_func2():
print("Function 2")
#funcdelay
def my_func3():
print("Function 3")
my_func1()
my_func2()
my_func3()
my_func1.nodelay()
my_func2.nodelay()
my_func3.nodelay()
Output:
Function 1
inner
Function 2
inner
Function 3
inner
Function 1
Function 2
Function 3
You can see that it can bypass the delay.
Not sure if I know what you mean but you could try:
functions = [my_func1, my_func2, my_func3]
for func in functions:
func()
time.sleep(1)
It's not a good way to handle delay in a function; because each function should do only one thing.
Dont't do this:
def my_func(delay):
# do stuff
if delay>0:
time.sleep(delay)
Try to make a delay handler function and put suitable delay after each function you pass to it.
Try this:
def delay_handler(functions_list,inputs_list,delay_list):
for function,cur_input,delay in zip(functions_list,inputs_list,delay_list):
function(*cur_input)
time.sleep(delay)
Tip 1: Zip will iterate throw each list (any iterable) simultaneously; first elements in inputs_list and delay_list are for first function in function_list and etc.
Tip 2: The '*' behind a list will unpack it.
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
I want to implement a timer to measure how long a block of code takes to run. I then want to do this across an entire application containing multiple modules (40+) across multiple directories (4+).
My timer is created with two functions that are within a class with a structure like this:
class SubClass(Class1)
def getStartTime(self):
start = time.time()
return start
def logTiming(self, classstring, start):
fin = time.time() - start
logging.getLogger('perf_log_handler').info((classstring + ' sec').format(round(fin,3)))
The first function gets the start time, and the second function calculates the time for the block to run and then logs it to a logger.
This code is in a module that we'll call module1.py.
In practice, generically, it will be implemented as such:
class SubSubClass(SubClass)
def Some_Process
stim = super().getStartTime()
code..............................
...
...
...
...
super().logTiming("The Process took: {}", stim)
return Result_Of_Process
This code resides in a module called module2.py and already works and successfully logs. My problem is that when structured like this, I can seemingly only use the timer inside code that is under the umbrella of SubClass, where it is defined (my application fails to render and I get a "can't find page" error in my browser). But I want to use this code everywhere in all the application modules, globally. Whether the module is within another directory, whether some blocks of code are within other classes and subclasses inside other modules, everywhere.
What is the easiest, most efficient way to create this timing instrument so that I can use it anywhere in my application? I understand I may have to define it completely differently. I am very new to all of this, so any help is appreciated.
OPTION 1) You should define another module, for example, "mytimer.py" fully dedicated to the timer:
import time
class MyTimer():
def __init__(self):
self.start = time.time()
def log(self):
now = time.time()
return now - self.start
And then, from any line of your code, for example, in module2.py:
from mytimer import MyTimer
class SomeClass()
def Some_Function
t = MyTimer()
....
t.log()
return ...
OPTION 2) You could also use a simple function instead of a class:
import time
def mytimer(start=None, tag=""):
if start is None:
start = time.time()
now = time.time()
delay = float(now - start)
print "%(tag)s %(delay).2f seconds." % {'tag': tag, 'delay': delay}
return now
And then, in your code:
from mytimer import mytimer
class SomeClass()
def Some_Function
t = mytimer(tag='BREAK0')
....
t = mytimer(start=t, tag='BREAK1')
....
t = mytimer(start=t, tag='BREAK2')
....
t = mytimer(start=t, tag='BREAK3')
return ...
I am not quite sure what you are looking for, but once upon a time I used a decorator for a similar type of problem.
The snippet below is the closest I can remember to what I implemented at that time. Hopefully it is useful to you.
Brief explanation
The timed is a 'decorator' that wraps methods in the python object and times the method.
The class contains a log that is updated by the wrapper as the #timed methods are called.
Note that if you want to make the #property act as a "class property" you can draw inspiration from this post.
from time import sleep, time
# -----------------
# Define Decorators
# ------------------
def timed(wrapped):
def wrapper(self, *arg, **kwargs):
start = time()
res = wrapped(self, *arg, **kwargs)
stop = time()
self.log = {'method': wrapped.__name__, 'called': start, 'elapsed': stop - start}
return res
return wrapper
# -----------------
# Define Classes
# ------------------
class Test(object):
__log = []
#property
def log(self):
return self.__log
#log.setter
def log(self, kwargs):
self.__log.append(kwargs)
#timed
def test(self):
print("Running timed method")
sleep(2)
#timed
def test2(self, a, b=2):
print("Running another timed method")
sleep(2)
return a+b
# ::::::::::::::::::
if __name__ == '__main__':
t = Test()
res = t.test()
res = t.test2(1)
print(t.log)
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