1

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?

1
  • 1
    FYI, there is no such thing as a simple script, all python code is equal whether it's a one liner or a gazillion lines framework. So, yes, you can use getattr if you want. Commented Mar 12, 2016 at 2:07

6 Answers 6

1

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()
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

1

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()

2 Comments

well, from what I can understand from the question and the exposed use case, it looks very likely to me that the OP is making the string list of methods from another source (maybe user input?),
It is a good one, I'm suggesting an improvement over it in my answer, though, using decorators. I also suggestva couple of alternatives that could be a better fit for the OP's needs.
0

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()

2 Comments

well, there's no good reason nor excuse to be using eval(). There's ALWAYS a better way.
Yes, that is why I wrote "not the best answer" in the first place!
0

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

Comments

0

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.

Resolving your methods within the scope (1/3)

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()

Resolving your methods within a class (2/3)

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()

Resolving your methods using a dict, registering them with a decorator (3/3)

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

1 Comment

You can use getattr on a module instance, e.g., x = __import__('foo'); getattr(x, 'func_a') (assuming module foo.py defines function func_x).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.