james_dean
james_dean

Reputation: 1517

Choosing the Function Dynamically

I've got some code that looks like the following:

if command == 'a':
    do_a(a, b, c)
elif command == 'b':
    do_b(a, b, c)
elif command == 'c':
    do_c(a, b, c)

How can I replace this type of thing with something more elegant? Perhaps, something along the lines of do_[command](a, b, c) where the function that is called is dependent upon the command?

Is it even possible?

Upvotes: 1

Views: 233

Answers (5)

Zeh
Zeh

Reputation: 331

def do(fname):
    return {'a':do_a, 'b':do_b, 'c':do_c}.get(fname)

def do_a(x,y,z): return x + y + z
def do_b(x,y,z): pass
def do_c(x,y,z): pass

Usage:

do('a')(1,2,3)
6

Upvotes: 1

gaurav
gaurav

Reputation: 91

This might help you, here souce can be any generic module path ending with function name like module.funcname

So a sample call would look like convertStringToFunction(module.funcname)

def import_module(name):
    mod = __import__(name)
    components = name.split('.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
return mod

def convertStringToFunction(source):
    tempArr = source.rsplit('.',1)
    mod = import_module(tempArr[0])
    func = getattr(mod, tempArr[1])
    return func

Upvotes: 0

sotapme
sotapme

Reputation: 4903

I'm waiting for a better solution from others but:

def dispatch(cmd, *args):
   globals()['do_' + cmd](*args)

def do_a(a, b, c): 

Usage:

dispatch('a', 1, 2, 3)

Obviously not robust but dispatch could make sure that the function exists.

Possibly a nice way would be to use decorators.

@command()
def do_a(a,b,c):
    ...

@command("do_x")
def myfunc(a, b, c):
   ...

Your decorator could maintain the dispatch lookup table etc.

It depends how worried you are that there might be a need to use existing functions or worry about name collisions.

Upvotes: 0

tzelleke
tzelleke

Reputation: 15345

You can store commands in a dict and lookup when needed:

In [15]: commands = {'mul': lambda x,y: x*y,
                     'add': lambda x,y: x+y}

In [16]: commands['mul'](3,4)
Out[16]: 12

In [17]: commands['add'](3,4)
Out[17]: 7

In [18]: command = 'add'; vars = (4,5)
In [19]: commands[command](*vars)
Out[19]: 9

you should check whether a command indeed is in commands:

if command in commands:
    commands[command]()
else:
    # handle error

Upvotes: 2

pcalcao
pcalcao

Reputation: 15990

You could do something like that using "reflection", calling the function by string name, like explained here:

Python: call a function from string name

BUT

That wouldn't be more elegant, it would be less readable, and easier to screw up if you don't have absolute control over what's passed as command.

Your version is just fine:

Explicit is better than implicit.

Simple is better than complex.

If you really want to avoid the elif's I would go with the dict of functions approach that was suggested in the comments.

Keep away from eval for things like this.

Upvotes: 1

Related Questions