How to get the class name of a method in Python? - python

Here's my problem and the code:
I try to use a decorator to reecord the time cost; but I cannot get the class name.
import functools
import time
def log_time(func):
#functools.wraps(func)
def record(*args, **kwargs):
# print(func)
# print(func.__name__)
# print(func.__class__)
# print(func.__class__.__name__)
func_name = func.__name__
start_time = time.time()
result = func(*args, **kwargs)
print(f"{func_name} costs time: {time.time() - start_time:.2f}s")
return result
return record
class FakeProject:
#log_time
def __init__(self, value):
self.load_data = [i for i in range(value)]
fake_project = FakeProject(100000000)
above code has log message as __init__ cost time: 3.65s;
But I want the FakeProject.__init__ cost time: 3.65s instead.
How can I get the class name and print it? Anyone can help? Thanks anyway
I try to print
print(func)
print(func.__name__)
print(func.__class__)
print(func.__class__.__name__)
and I get
<function FakeProject.__init__ at 0x0000022F3F365318>
__init__
<class 'function'>
function

In this particular case, you can just use the __qualname__:
import functools
import time
def log_time(func):
#functools.wraps(func)
def record(*args, **kwargs):
func_name = func.__qualname__
start_time = time.time()
result = func(*args, **kwargs)
print(f"{func_name} costs time: {time.time() - start_time:.2f}s")
return result
return record
class FakeProject:
#log_time
def __init__(self, value):
self.load_data = [i for i in range(value)]
fake_project = FakeProject(100000000)
This outputs:
FakeProject.__init__ costs time: 5.26s

Related

Python chain several functions into one

I have several string processing functions like:
def func1(s):
return re.sub(r'\s', "", s)
def func2(s):
return f"[{s}]"
...
I want to combine them into one pipeline function: my_pipeline(), so that I can use it as an argument, for example:
class Record:
def __init__(self, s):
self.name = s
def apply_func(self, func):
return func(self.name)
rec = Record(" hell o")
output = rec.apply_func(my_pipeline)
# output = "[hello]"
The goal is to use my_pipeline as an argument, otherwise I need to call these functions one by one.
Thank you.
You can write a simple factory function or class to build a pipeline function:
>>> def pipeline(*functions):
... def _pipeline(arg):
... result = arg
... for func in functions:
... result = func(result)
... return result
... return _pipeline
...
>>> rec = Record(" hell o")
>>> rec.apply_func(pipeline(func1, func2))
'[hello]'
This is a more refined version written with reference to this using functools.reduce:
>>> from functools import reduce
>>> def pipeline(*functions):
... return lambda initial: reduce(lambda arg, func: func(arg), functions, initial)
I didn't test it, but according to my intuition, each loop will call the function one more time at the python level, so the performance may not be as good as the loop implementation.
You can just create a function which calls these functions:
def my_pipeline(s):
return func1(func2(s))
Using a list of functions (so you can assemble these elsewhere):
def func1(s):
return re.sub(r'\s', "", s)
def func2(s):
return f"[{s}]"
def func3(s):
return s + 'tada '
def callfuncs(s, pipeline):
f0 = s
pipeline.reverse()
for f in pipeline:
f0 = f(f0)
return f0
class Record:
def __init__(self, s):
self.name = s
def apply_func(self, pipeline):
return callfuncs(s.name, pipeline)
# calling order func1(func2(func3(s)))
my_pipeline = [func1, func2, func3]
rec = Record(" hell o")
output = rec.apply_func(my_pipeline)

Change the signature of a function inside a class via decorator

I already have a class poly(), and a method get_function()
class poly():
def __init__(self,n):
func = ''
var = []
for i in range(n + 1):
func += ('k'+str(i)) + ' * '+ 'x ** ' + str(i) + ' + '
var.append('k'+str(i))
func = func[:-3]
self.func_str = func
self.var = var
siglist = var.copy()
siglist.append('x')
self.siglist = tuple(siglist)
def get_function(self, *args):
return eval(self.func_str)
Now what I want to do is to pass self.siglist to signature of get_function for future usage(scipy.optimize.curve_fit needs __signature__ to do curve fitting)
I ran
pol = poly(2)
inspect.signature(pol.get_function)
It shows that the signature of that function is <Signature (*args)>
But I want to change signature from *args to k0, k1, x(Stored in tuple siglist)
What I found in Python Cookbook is:
from functools import wraps
import inspect
def optional_debug(func):
#wraps(func)
def wrapper(*args, debug=False, **kwargs):
return func(*args, **kwargs)
sig = inspect.signature(func)
parms = list(sig.parameters.values())
# what is inspect.Parameter.KEYWORD_ONLY do ?
parms.append(inspect.Parameter('debug', inspect.Parameter.KEYWORD_ONLY, default=False))
wrapper.__signature__ = sig.replace(parameters=parms)
return wrapper
#optional_debug
def test(input):
pass
print(inspect.signature(test))
This function is able to change the signature of a function, the result is:
(input, *, debug=False)
How to pass self.siglist to edit the signature if I put the decorator outside the class, and why is there a * in the signature after using a decorator to change it?
________________To edit the __signature__ if a function is not in a class___________
def make_sig(*names):
parms = [Parameter(name, Parameter.POSITIONAL_OR_KEYWORD)
for name in names]
return Signature(parms)
def test():
pass
ls = ('a','b')
test.__signature__ = make_sig(*ls)
inspect.signature(test)
get:
<Signature (a, b)>
But What about it inside a class?

Timeit module for qgis plugin

I'd like to use the python module timeit to time some functions in my QGIS plugin.
Here, I've called the time it function within a function that I call at the end of the last function. It seems, though, that the plugin is taking even longer to run than usual and I am wondering if i'm calling the timer in the wrong place. Is there a better way to set this up?
class myPluginName:
def firstFunction(self):
...
self.secondFunction()
def secondFunction(self):
...
self.timeThings()
def run(self):
self.firstFunction()
def timeThings(self):
QMessageBox.information(None, 'First Function', 'Time : %s' % timeit.timeit(self.firstFunction,number=1)
QMessageBox.information(None, 'Second Function', 'Time : %s' % timeit.timeit(self.secondFunction,number=1)
UPDATE: After following some advice, i've tried to implement the wrapper in the following way. I get however, a TypeError: firstFunction() takes exactly 1 argument (2 given) on ret = func(**args, **kwargs)
def time_func(func):
try:
name = func.__name__
except:
name = func.f__name
def tf_wrapper(*args, **kwargs):
t = time.time()
ret = func(*args, **kwargs)
QMessageLog.logMessage("{}: {}".format(name, time.time() - t))
return ret
return tf_wrapper
class myPlugin:
def initGui(self):
QObject.connect(self.dlg.ui.comboBox,SIGNAL("currentIndexChanged(int)"), self.firstFunction)
#time_func
def firstFunc(self):
registry = QgsMapLayerRegistry.instance()
firstID = str(self.dlg.ui.firstCombo.itemData(self.dlg.ui.firstCombo.currentIndex()))
secondID = str(self.dlg.ui.secondCombo.itemData(self.dlg.ui.secondCombo.currentIndex()))
self.firstLayer = registry.mapLayer(firstID)
self.secondLayer = registry.mapLayer(secondID)
#time_func
def secondFunc(self):
...
self.thirdFunc()
def thirdFunct(self):
...
def run(self):
self.dlg.ui.firstCombo.clear()
self.dlg.ui.secondCombo.clear()
for layer in self.iface.legendInterface().layers():
if layer.type() == QgsMapLayer.VectorLayer:
self.dlg.ui.firstCombo.addItem(layer.name(), layer.id())
self.dlg.ui.secondCombo.addItem(layer.name(), layer.id())
result = self.dlg.exec_()
if result == 1:
self.secondFunction()
OK, I don't know your exact situation, but I'd set it up though decorators:
import time
def time_func(func):
try:
name = func.__name__
except:
name = func.f_name
def tf_wrapper(*args, **kwargs):
t = time.time()
ret = func(*args, **kwargs)
print("{}: {}".format(name, time.time() - t)) # Or use QMessageBox
return ret
return tf_wrapper
class myPluginName:
#time_func
def firstFunction(self):
pass
#time_func
def secondFunction(self):
pass
def run(self):
self.firstFunction()
myPluginName().firstFunction()
With this code, any function wrapped in time_func will have the time taken to execute the function printed when it returns, along with its name. E.g. running it will print:
firstFunction: 1.430511474609375e-06
For your TypeError, you need to change;
def firstFunction(self):
pass
To:
def firstFunction(self, index):
pass

Return list function python

I am new to python and I'm trying this code below q Objects1 return to list
how can I do this?
it returns me the following error
File "/ home/paulo/Desktop/testepy2/objectMIB.py", line 53
     return
SyntaxError: 'return' outside function
thank you
from pysnmp.entity import engine, config
from pysnmp import debug
from pysnmp.entity.rfc3413 import cmdrsp, context, ntforg
from pysnmp.carrier.asynsock.dgram import udp
from pysnmp.smi import builder
import threading
import collections
import time
MibObject = collections.namedtuple('MibObject', ['mibName',
'objectType', 'valueFunc'])
class Mib(object):
"""Stores the data we want to serve.
"""
def __init__(self):
self._lock = threading.RLock()
self._test_count = 0
self._test_get = 10
self._test_set = 0
def getTestDescription(self):
return "My Description"
def getTestCount(self):
with self._lock:
return self._test_count
def setTestCount(self, value):
with self._lock:
self._test_count = value
def getTestGet(self):
return self._test_get
def getTestSet(self):
return self._test_set
def setTestSet(self):
self._test_set = value
class ListObejtc ():
mib = objectMIB.Mib()
objects1 = [MibObject('MY-MIB', 'testDescription', mib.getTestDescription),
MibObject('MY-MIB', 'testCount', mib.getTestCount),MibObject('MY-MIB', 'testGet', mib.getTestGet), MibObject('MY-MIB', 'testSet', mib.getTestSet) ]
print objects1
return
It's normal for the code you have shown nested inside "ListObejtc" to be in a method, like so:
class ListObejtc ():
def __init__(self):
pass
def doObjects(self):
mib = objectMIB.Mib()
objects1 = [MibObject('MY-MIB', 'testDescription', mib.getTestDescription),
MibObject('MY-MIB', 'testCount', mib.getTestCount),MibObject('MY-MIB', 'testGet', mib.getTestGet), MibObject('MY-MIB', 'testSet', mib.getTestSet) ]
print objects1
return objects1
You got a SyntaxError because the return as you had it was in class context, and it makes no sense there.

Python/Django: Are there any decorators to tell that input is not none?

I am new to Django and come from Java/Spring background.
I am wondering if there are decorators something like following that can be done in Django or Python?
Want
def addToList(#not_none a, #not_none b):
# so that I do not check for nullity explicitly
do_things_with_a(a)
do_things_with_b(b)
Since this is something which is pretty easy to get in Java, just looking if Python/Django has it
One doesn't typically constraint data-types in Python. Also, decorators can only be applied to classes and to methods/functions.
Although, you shouldn't really be doing this, this is how you would.
(You could amend this to accept argument names to enforce constraints on with a little work).
def not_none(f):
def func(*args, **kwargs):
if any(arg is None for arg in args):
raise ValueError('function {}: does not take arguments of None'.format(f.__name__))
return f(*args, **kwargs)
return func
#not_none
def test(a, b):
print a, b
You can write a decorator rejectNone as follows:
def rejectNone(f):
def myF(*args, **kwargs):
if None in args or None in kwargs.values():
raise Exception('One of the arguments passed to {0} is None.'.format(f.__name__)
return f(*args, **kwargs)
return myF
#rejectNone
def f(a, b, k=3):
print a * b
You will now get an Exception if you try to call f with a None argument. Note that decorators can be applied to functions or class methods but you can't put them in front of function parameters.
I know this is late, but to those who it may be helpful.
I have a simple repo based off of Jon's answer that accepts arguments for nullable fields here.
def not_none(nullable_parameters=None):
def the_actual_test(f, args, filter_array):
has_none = False
bad_parameters = []
if type(filter_array) is str:
filter_array = [filter_array]
if not filter_array:
if any(arg[1] is None for arg in args):
raise ValueError('function {}: Parameters cannot be None. '.format(f.__name__))
elif type(filter_array) is list:
for a in args:
for ff in filter_array:
if a[0] != ff:
if a[1] is None:
has_none = True
bad_parameters.append(a[0])
break
if has_none:
raise ValueError('function {}: Parameters {} cannot be None. '.format(f.__name__, bad_parameters))
def real_decorator(f):
v_names = f.__code__.co_varnames
def wrapper(*args, **kwargs):
n_args = []
for a in range(0, len(args)):
n_args.append((v_names[a], args[a]))
the_actual_test(f, n_args, nullable_parameters)
result = f(*args, **kwargs)
return result
return wrapper
return real_decorator
Usage
from not_none import not_none
#not_none()
def no_none(a,b):
return (a,b)
#not_none(nullable_parameters=["b"])
def allow_b_as_none(a,b):
return (a,b)
#passes
no_none(1,1)
#fails
no_none(None,1)
#passes
allow_b_as_none(1,None)
#fails
allow_b_as_none(None,1)
After my first answer got deleted. Here is an updated version:
I tried to use the very nice answer from Bigbob556677, but for me it didn't work with **kwargs, so I edited it and put it in a Gist, here: https://gist.github.com/devTechi/6e633ded72cc83637f34b1a3f4a96984 (code also below)
I didn't test it with just *args, but with (I posted more or less just the gist-link) **kwargs it works nicely.
def not_none(nullable_parameters=None):
# values given by real_decorator (see below)
def the_actual_test(f, expected_args_with_given, allowed_nullable_args):
has_none = False
bad_parameters = []
for key, value in expected_args_with_given.items():
if (value is None and nullable_parameters is None) or \
(value is None and key not in nullable_parameters):
bad_parameters.append(key)
has_none = True
if has_none:
raise ValueError("[Function '{}' of '{}'] - IMPORTANT: Parameters '{}' cannot be None. ".format(f.__name__, f.__module__, bad_parameters))
# here the code REALLY begins
def not_null_decorator(original_func):
import inspect
has_self = False
# f.__code__.co_varnames --> local variables (not only parameters), see: https://python-reference.readthedocs.io/en/latest/docs/code/varnames.html
# get declared arguments from ogirinal function
argspec = inspect.getargspec(original_func)
if 'self' in argspec.args:
argnames = argspec.args[1:] # no self
has_self = True
else:
argnames = argspec.args
args_dict = dict.fromkeys(argnames)
def get_args(*args, **kwargs):
for arg in args:
if arg in args_dict.keys():
args_dict[arg] = arg
for key, value in kwargs.items():
if key in args_dict.keys():
args_dict[key] = value
return args_dict
def wrapper_with_self(self, *args, **kwargs):
the_actual_test(original_func, get_args(*args, **kwargs), nullable_parameters)
return original_func(self, *args, **kwargs)
def wrapper(*args, **kwargs):
the_actual_test(original_func, get_args(*args, **kwargs), nullable_parameters)
return original_func(*args, **kwargs)
if has_self:
return wrapper_with_self
else:
return wrapper
return not_null_decorator
Usage:
from .nullable_decorator import not_none
#not_none(nullable_parameters=["nullable_arg1", "nullable_arg2"])
def some_function(self, nullable_arg1=None, nullable_arg2=None, non_nullable_arg1=None):
pass
#not_none()
def some_other_function(self, non_nullable_arg1=None, non_nullable_arg2=None):
pass

Categories