Decorator strange behavior [duplicate] - python

This question already has answers here:
python decorator TypeError missing 1 required positional argument
(3 answers)
How to create a decorator that can be used either with or without parameters?
(16 answers)
Closed 2 years ago.
This is meant to calculate execution time and log / print it:
def timer(logger=None):
def decorator(func):
def wrapper(*args, **kwargs):
start_time = perf_counter()
result = func(*args, **kwargs)
total_time = perf_counter() - start_time
message = f'{func.__name__} Time: ' f'{total_time} seconds'
if logger is not None:
logger.info(message)
else:
print(message)
return result
return wrapper
return decorator
#timer
def decorated():
print("Running ...")
if __name__ == '__main__':
decorated()
I'm expecting this to print time, instead it complains about a missing argument:
TypeError: decorator() missing 1 required positional argument: 'func'
However, when I do:
#timer(None)
def decorated():
print("Running ...")
or:
#timer(logger=None)
def decorated():
print("Running ...")
It works! what is this nonsense?
Running ...
decorated Time: 2.3044999999999316e-05 seconds
Notes:
To those marking my question as a duplicate, can you explain why
other decorators work just fine?
In the example below, lru_cache accepts maxsize and typed parameters and works without explicit calls as well.
Example:
from functools import lru_cache
#lru_cache
def decorated():
print("Running ...")
Out:
Running ...

Related

How to cache a function using a decorator (Python)

I'm studying Python decorators, and I'm trying to write my own cache function (to better understand what caching is about).
This is what I tried:
def hash_table_fun(func):
hash_table = {}
def wrapper(*args):
if args not in hash_table:
hash_table[args] = func(*args)
return hash_table[args]
return wrapper
#hash_table_fun
def slow_function(a,b):
time.sleep(5)
return a + b
start_time = time.time()
print(slow_function(2,3))
print(f"Run time: {time.time() - start_time}")
The first time I call slow_function with the arguments 2 and 3, the function takes about 5s, as expected. But when I call slow_function a second time with the same arguments, it still takes 5s. What am I doing wrong?

How to obtain the cost time of each **def**? [duplicate]

This question already has answers here:
How do I profile a Python script?
(33 answers)
Closed 2 years ago.
I create a decorator to time the cost of every funcions :
def timmer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
stop_time = time.time()
print('Func %s, run time: %s' % (func.__name__, stop_time - start_time))
return res
return wrapper
And use it every def , for example :
#timmer
def Test_func(*args,**kwargs):
return "hello !"
Then I can obtain the cost time of every func.
But if I do this , I have to use #timmer in every def .If this .py file has 100 defs , then I must use #timmer for 100 times.
How can I get the running time of each method conveniently ?
Create a master function which in turn calls any one of the 100 functions with the function name as a parameter
Use the decorator with this master function
Call the master function in a loop for all the 100 functions and save results in a list.append()

Python multiprocessing pool map with multiple arguments [duplicate]

This question already has answers here:
How to use multiprocessing pool.map with multiple arguments
(23 answers)
Closed 5 years ago.
I have a function to be called from multiprocessing pool.map with multiple arguments.
from multiprocessing import Pool
import time
def printed(num,num2):
print 'here now '
return num
class A(object):
def __init__(self):
self.pool = Pool(8)
def callme(self):
print self.pool.map(printed,(1,2),(3,4))
if __name__ == '__main__':
aa = A()
aa.callme()
but it gives me following error
TypeError: printed() takes exactly 2 arguments (1 given)
I have tried solutions from other answers here but they are not working for me.
How can i resolve it and what is the reason for this problem (I did not get the pickle POV)
You should be giving args in array
from multiprocessing import Pool
import time
def printed(*args):
print 'here now '
return args[0][0]
class A(object):
def __init__(self):
self.pool = Pool(8)
def callme(self):
print self.pool.map(printed,[(1,2),(3,4)])
if __name__ == '__main__':
aa = A()
aa.callme()

How can I rate limit a function in Python 2.7 with a decorator? [duplicate]

This question already has answers here:
Read/Write Python Closures
(8 answers)
Closed 8 years ago.
I'm trying to write a decorator in Python to limit the number of times a function is called in an amount of time. I anticipate using it like this:
#ratelimit(seconds=15)
def foo():
print 'hello'
start = time.time()
while time.time() - start < 10:
foo()
> 'hello'
> 'hello'
So the decorated function can be called a maximum of once every seconds. In terms of implementing it I have this, but it doesn't work as I'm not sure the correct way to persist the last_call between subsequent calls:
import time
def ratelimit(seconds=10):
last_call = None # Never call decorated function
def decorator(func):
def wrapper(*args, **kwargs):
if last_call is None or time.time() - last_call > seconds:
result = func(*args, **kwargs)
last_call = time.time()
return result
return wraps(func)(wrapper)
return decorator
The code below worked fine for me in python 2.7.
import time
from functools import wraps
last_called = dict() # When last called, and with what result
def ratelimit(seconds=10, timer=time.time):
def decorator(func):
last_called[func] = None
#wraps(func)
def wrapper(*args, **kwargs):
now = timer()
call_data = last_called.get(func, None)
if call_data is None or now - call_data[0] >= seconds:
result = func(*args, **kwargs)
last_called[func] = (now, result)
else:
result = call_data[1] # Replay rate-limited result
return result
return wrapper
return decorator

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