Reputation: 37
I am testing multiple algorithms, and want to be able to add algorithms quickly. To do so I want to put algorithms in a list as a string, and then use the string to call the method. I'm also doing a simple timing of the algorithms and don't want to duplicate the timer code between each method.
For example:
testData = (testSet1, testSet2, testSet3,...,testSetN)
methods = ("method1", "method2", "method3",...,"methodN")
for x in testData:
for y in methods:
startTime = time.time()
#call y with x as parameter life
endTime = time.time()
I can't use getattr(y, x)
because everything is written as a simple script. Is there an easy way to do this, or will I need to call each method?
Upvotes: 1
Views: 175
Reputation: 24812
Here I'm suggesting you 3 methods to achieve what you want, from the easiest but most dirty (#1), to the most elegant one (#3). It's up to you to choose what's best fitted for you use case.
well, depending on the context which you'll execute the code, you can use locals()
or globals()
:
>>> def foo():
... print("XXX")
...
>>> locals()['foo']
<function foo at 0x266e9b0>
>>> locals()['foo']()
XXX
or
>>> globals()['foo']
<function foo at 0x266e9b0>
>>> globals()['foo']()
XXX
Because I'm at the interpreter's level. Those should work directly within a module's file.
So then, your code would become:
testData = (testSet1, testSet2, testSet3,...,testSetN)`
methods = ("method1", "method2", "method3",...,"methodN")
for data in testData:
for method in methods:
startTime = time.time()
globals()[method](data)
endTime = time.time()
A rather better way — at least IMHO — to do it would be to have all your method within a class, and thus you actually CAN use getattr()
:
>>> class Foo:
... def bar(self):
... print("FOOBAR!")
...
>>> f = Foo()
>>> getattr(f, 'bar')
<bound method Foo.bar of <__main__.Foo instance at 0x266d908>>
>>> getattr(f, 'bar')()
FOOBAR!
but I can understand that you would not want to create an instance for a class that is just there to keep together a bunch of methods, so you could instead use class methods:
>>> class Bar:
... @classmethod
... def foo(self):
... print("BARFOO!")
...
>>> getattr(Bar, 'foo')
<bound method classobj.foo of <class __main__.Bar at 0x7f98619e9940>>
>>> getattr(Bar, 'foo')()
BARFOO!
So your could would become:
class MyAlgorithms:
@classmethod
def method1():
pass
@classmethod
def method2():
pass
…
testData = (testSet1, testSet2, testSet3,...,testSetN)`
methods = ("method1", "method2", "method3",...,"methodN")
for data in testData:
for method in methods:
startTime = time.time()
if method in dir(MyAlgorithms):
getattr(MyAlgorithms, method)(data)
endTime = time.time()
And finally, my favourite way to do such a thing, is to use a dict like @RafaelCardoso suggests, but with a bit of salt and pepper:
>>> registered_methods = {}
>>> def register_method(fun):
... registred_methods[fun.__name__] = fun
... return fun
...
>>> @register_method
... def foo():
... print("XXX")
...
>>> registered_methods
{'foo': <function foo at 0x7faf37b23320>}
>>> registered_methods['foo']()
XXX
The idea is to create a global dict named methods
that will contain the mapping from string to actual method, then you create a decorator called register_method
, and finally you just have to decorate the method when you write it down, and it'll be added to the global methods
dict!
So your could would be:
testData = (testSet1, testSet2, testSet3,...,testSetN)
methods = ("method1", "method2", "method3",...,"methodN")
for data in testData:
for method in methods:
if method in registered_methods:
startTime = time.time()
registered_methods[method](data)
endTime = time.time()
N.B.: in my examples, I considered that methods
is the list of methods you want to test, not the list of methods you actually have.
HTH
Upvotes: 0
Reputation: 1295
Why don't you just make you functions name in a tuple/list instead of function name string to call them.
testData = (testSet1, testSet2, testSet3,...,testSetN)
methods = (method1, method2, method3,...)
for x in testData:
for method in methods:
startTime = time.time()
method(x)
endTime = time.time()
Upvotes: 1
Reputation: 17552
Use globals()
which is a built-in function. From documentation for globals()
:
Return a dictionary representing the current global symbol table. This is always the dictionary of the current module (inside a function or method, this is the module where it is defined, not the module from which it is called).
However, if your methods are in some local scope, you can locals()
instead of globals
.
Try this (using globals
):
def method1(arg):
print(method1.__name__, 'called with', arg)
def method2(arg):
print(method2.__name__, 'called with', arg)
def method3(arg):
print(method3.__name__, 'called with', arg)
methods = ('method1', 'method2', 'method3')
testData = ('testSet1', 'testSet2', 'testSet3')
for x in testData:
for y in methods:
globals()[y](x)
Output:
method1 called with testSet1
method2 called with testSet1
method3 called with testSet1
method1 called with testSet2
method2 called with testSet2
method3 called with testSet2
method1 called with testSet3
method2 called with testSet3
method3 called with testSet3
Upvotes: 0
Reputation: 2691
This is not the best answer, but to complete the possibilities
import time
testSet1 = 1
testSet2 = 2
def method1(s): print (10*s)
def method2(s): print (100*s)
def method3(s): print (1000*s)
testData = (testSet1, testSet2 )
methods = ("method1", "method2", "method3")
for x in testData:
for y in methods:
startTime = time.time()
f = eval("lambda ts: %s(ts)" % y)
f(x)
endTime = time.time()
Upvotes: 0
Reputation: 213308
A simple way to do this would be to create a decorator which adds the functions to a dictionary.
methods = {}
def method(func):
methods[func.__name__] = func
return func
@method
def method1(arg):
print('Method #1 called, arg:' arg)
@method
def method2(arg):
print('Method #2 called, arg:', arg)
methods['method1'](16)
If the methods are part of a class, other techniques should be used.
There are dozens of ways of accomplishing this particular task. Python overflows with different techniques for metaprogramming.
Upvotes: 1
Reputation: 59274
Use a dictionary
testData = (testSet1, testSet2, testSet3,...,testSetN)`
methods = {"method1":method1, "method2":method2, "method3":method3,...,"methodN":methodN}
for x in testData:
for y in methods.keys():
startTime = time.time()
methods[y](x)
endTime = time.time()
Upvotes: 1