I am trying to parametrize a function using decorator. Finally I am able to get it run as expected after lot of hit and trials. But still I am not satisfied as though it is working, it doesn't seem to be right way to do it.
Please help me improve this code.
Here is my code:
def WarmWelcome(fn):
def wrapped(DataProvider):
for name in DataProvider():
print fn(name) + ":)"
return wrapped
def DataProvider():
names=["abc","xyz","def"]
for name in names:
yield name
#WarmWelcome
def hello(name):
return "hello " +name
hello(DataProvider)
Here is the updated code:
def WarmWelcome(DataProvider):
def real_decorator(fn):
def wrapped():
for name in DataProvider():
print fn(name) + ":)"
return wrapped
return real_decorator
def DataProvider():
names=["abc","xyz","def"]
for name in names:
yield name
#WarmWelcome(DataProvider)
def hello(name):
return "hello " +name
hello()
It's also possible to provide dataset right to WarmWelcome decorator:
def WarmWelcome(*data_sets):
def _decorator(func):
def _func():
for ds in data_sets:
func(*ds)
return _func
return _decorator
#WarmWelcome(
("abc", ),
("xyz", ),
("def", ),
)
def hello(name):
return "hello " +name
Original: PHPUnit-like dataProvider implementation for Python unittest
Related
This question already has an answer here:
Is it possible to programmatically decorate a function/method at runtime?
(1 answer)
Closed last year.
I have got the following decorators:
def msg_factory(*, prefix: str, **styles: Dict[str, Any]):
def decorator_msg_factory(func):
#functools.wraps(func)
def wrapper_msg_factory(*args, **kwargs):
msg = func(*args, **kwargs)
return typer.echo(f"{typer.style(prefix, **styles)}: {msg}")
return wrapper_msg_factory
return decorator_msg_factory
error_msg = msg_factory(prefix="ERROR", fg=typer.colors.BLACK, bg=typer.colors.RED)
warn_msg = msg_factory(prefix="WARN", fg=typer.colors.BLACK, bg=typer.colors.YELLOW)
info_msg = msg_factory(prefix="INFO", fg=typer.colors.BLACK, bg=typer.colors.GREEN)
I can use it like this:
#info_msg
def init_template_created_successfully() -> str:
msg_0 = "sdasdasd:\n\n"
msg_1 = "1. dasdasd\n"
return msg_0 + msg_1
This works fine, but I want to use a factory function for the messages like this:
def create_multiline_message_function(*messages: str) -> str:
def multiline_string():
return "\n".join(messages)
return multiline_string
init_template_created_successfully = create_multiline_message_function(
"1. sdasda",
"2. sdasda"
)
This does not work unfortunately and results in a syntax Error. It my approach possible? If yes, how ?
#info_msg
init_template_created_successfully
Python decorators are syntax-sugar, so
#mydecorator
def myfunction():
...
is equivalent to
def myfunction():
...
myfunction = mydecorator(myfunction)
as you might observe latter might be applied independetly from def. For futher discussion see PEP 318.
I am trying to learn decorators, so I have implemented the following example, where I am trying to create contents inside a specific tag.
def content_decoration(func1):
def inner_function(name, c_decorator, tag):
return '<{1}> {0} </{1}>'.format(func1(name, c_decorator), tag)
return inner_function
#content_decoration
def return_decorated_content(content , con_decorator):
return '{0} {1}'.format(content, con_decorator)
return_decorated_content('Content', ' content_addition', 'p')
The output of the above command would be:
'<p> Content content_addition </p>'
However I find it a bit difficult, when I need to decorate both the content and the tag itself. For example, we have the following code:
def decoration(func1, func2):
def inner_function(content, tag, content_decoration=None, tag_decoration=None):
return '<{1}> {0} </{1}>'.format(func1(content, content_decoration ), func2(tag, tag_decoration))
return inner_function
def name_decoration(con, con_decor):
return '{0} {1}'.format(con, con_decor)
def tag_decoration(tag, tag_decor):
return '{0} {1}'.format(tag, tag_decor)
Without the use of decorators we would have:
print decoration(name_decoration, tag_decoration)(content='Alice', tag='h1', tag_decoration='fontsize=12', content_decoration='')
# or
print
function = decoration(name_decoration, tag_decoration)
print function(content='Bob', content_decoration='Smith', tag='p')
which yields:
<h1 fontsize=12> Alice </h1 fontsize=12>
<p None> Bob Smith </p None>
but how can I achieve the same result using the python syntactic sugar?
You can declare the name and tag functions above the function to be decorated and pass as parameters to the outer decorator:
def decoration(func1, func2):
def wrapper(f1):
def inner_function(content, tag, content_decoration=None, tag_decoration=None):
return '<{1}> {0} </{1}>'.format(func1(content, content_decoration ), func2(tag, tag_decoration))
return inner_function
return wrapper
def name_decoration(con, con_decor):
return '{0} {1}'.format(con, con_decor)
def tag_decoration(tag, tag_decor):
return '{0} {1}'.format(tag, tag_decor)
#decoration(name_decoration, tag_decoration)
def get_html(content, tag, content_decoration=None, tag_decoration=None):
return
print(get_html(content='Alice', tag='h1', tag_decoration='fontsize=12', content_decoration=''))
print(get_html(content='Bob', content_decoration='Smith', tag='p'))
Output:
<h1 fontsize=12> Alice </h1 fontsize=12>
<p None> Bob Smith </p None>
Or, you can use lambda functions to save space:
#decoration(lambda con, con_decor:'{0} {1}'.format(con, con_decor), lambda tag, tag_decor:'{0} {1}'.format(tag, tag_decor))
def get_html(content, tag, content_decoration=None, tag_decoration=None):
return
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
My version of Python does not support
#unittest.skip("demonstrating skipping")
from Disable individual Python unit tests temporarily, I understand how to use a decorator to achieve this, i.e.,
def disabled(f):
def _decorator():
print f.__name__ + ' has been disabled'
return _decorator
#disabled
def testFoo():
'''Foo test case'''
print 'this is foo test case'
testFoo()
However, the decorator does not support providing a message for the skipping. May I know how I can achieve this please? I basically want something like
def disabled(f, msg):
def _decorator():
print f.__name__ + ' has been disabled' + msg
return _decorator
#disabled("I want to skip it")
def testFoo():
'''Foo test case'''
print 'this is foo test case'
testFoo()
You can modify the decorator like so:
def disabled(msg):
def _decorator(f):
def _wrapper():
print f.__name__ + ' has been disabled ' + msg
return _wrapper
return _decorator
#disabled("I want to skip it")
def testFoo():
'''Foo test case'''
print 'this is foo test case'
testFoo()
i have made a program but the output that i'm getting is
(<q3v3.Student instance at 0x023BB620>, 'is doing the following modules:', ' <q3v3.Module instance at 0x023BB670> <q3v3.Module instance at 0x023BB698>')
For example , the above output should give me Alice is doing following module : biology, chemistry
Help
this is my full code:
class Student :
def __init__(self,students):
self.students= students
print self.students
#def __str__(self): # when i used this i've got error type TypeError: __str__ returned non-string (type NoneType)
#print str(self.students)
class Module:
def __init__(self,modules):
self.modules = modules
print self.modules
#def __str__(self):
#print str(self.modules)
class Registrations (Student,Module):
def __init__(self):
self.list= []
self.stulist = []
self.modulist= []
def __iter__(self):
return iter(self.list)
def __str__(self):
return str(self.list)
def add(self,students,modules):
self.list.append((students,modules))
#print (self.list)
def students(self,modules):
for i in self.list:
if i[1] == modules:
self.modulist.append((i[0]))
return iter(self.modulist)
def __str__(self):
return str(self.students)
def modules(self,students):
for i in self.list:
if i[0] == students:
self.stulist.append((i[1]))
return iter(self.stulist)
def __str__(self):
return str(self.modules)
i need to import my program to be able to run it to this :
from q3v4 import *
james = Student('james')
alice = Student('alice')
mary = Student('mary')
agm = Module('agm')
ipp = Module('ipp')
r = Registrations()
r.add(james,agm)
r.add(alice,agm)
r.add(alice,ipp)
mstr = ''
for m in map(str,r.modules(alice)):
mstr = mstr+' '+m
print(alice, 'is doing the following modules:', mstr)
sstr = ''
for s in map(str,r.students(agm)):
sstr = sstr+' '+s
print(agm, 'has the following students:', sstr)
print(r)
You could define a __str__ method in your Student class, and do something like this:
def __str__(self):
return self.name # Here the string you want to print
Are you using Python 2? If so, print is a keyword, not a function. There are two ways to solve your problem:
Write print foo, bar instead of print(foo, bar).
The difference is that print(foo, bar) is actually printing out the tuple (foo, bar), which uses the repr() representation of each element, rather than its str().
At the very top of your file, write from __future__ import print_function. This will magically convert print from a keyword into a function, causing your code to work as expected.
If you are using Python 3, my answer is irrelevant.