dobber1611
dobber1611

Reputation: 37

Given a string, call the method with the same name

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

Answers (6)

zmo
zmo

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.

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

Upvotes: 0

Ekeyme Mo
Ekeyme Mo

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

Sнаđошƒаӽ
Sнаđошƒаӽ

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

Sci Prog
Sci Prog

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

Dietrich Epp
Dietrich Epp

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

rafaelc
rafaelc

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

Related Questions