5

I need to write a method that takes in 3 arguments:

  1. a string with the name of a function
  2. an ordered list of arguments to that function. This includes arguments with default values and *varargs, but does not include **kwargs
  3. a dict representing any additional keyword arguments, or None if there are none

And I need to use this input to retrieve a function and call it. For example:

def dispatch(name, args, kwargs=None):
    do_magic_here(name, args, kwargs)

def meth1():
    print "meth1"

def meth2(a, b):
    print "meth2: %s %s" % (a, b)

def meth3(a, **kwargs):
    print "meth3: " + a
    for k,v in kwargs.iteritems():
        print "%s: %s" % (k,v)

And I need to be able to call things like this:

>>> dispatch("meth1", [])
meth1
>>> dispatch("meth2", [1, 3])
meth2: 1 3
>>> dispatch("meth3", [1], {"hello":2, "there":3})
meth3: 1
hello: 2
there: 3

I could do this:

def do_magic_here(name, args, kwargs=None):
    if name=="meth1":
        meth1()
    if name=="meth2":
        meth2(args[0], args[1])
    if name=="meth3":
        meth3(args[0], **kwargs)

But I'm trying to dispatch like 40 methods, and that number may expand, so I'm hoping there's a more programmatic way to do it. I'm looking at something with getattr, but I can't quite figure it out.

2 Answers 2

7

I would just use

def dispatch(name, *args, **kwargs):
    func_name_dict[name](*args, **kwargs)

with

func_name_dict = {'meth1':meth1,
                  'meth2':meth2,
                  ...}

Allowing you to pass args and kwargs through more naturally and transparently:

>>> dispatch("meth2", 1, 3)
meth2: 1 3

You can of course use globals() or locals() in place of the dict, but you might need to be careful about which functions in each namespace you do or don't want to expose to the caller

Sign up to request clarification or add additional context in comments.

7 Comments

It's notable that dispatch does not take *args or **kwargs, but rather an actual list and dictionary. does that make a difference? It must be defined this way, because of the framework in which I'm working
Unless you really have to pass lists and dicts through, I would do it this way; it's the more natural way to pass unknown args around in Python (e.g. decorators, method overrides)
@ewok You can still unpack lists via * and dicts via **. Maybe you should change the default value for kwargs to {}
@ewok just to elaborate on schwobaseggl's point, you can now use dispatch(*args, **kwargs)
@HumphreyTriscuit i think he means that if he passed in say [1], {'a':2} these would get interpreted as args where the first arg is a list and second is a dict. To get around that you could do something like dispatch = lambda name, args, kwargs: func_name_dict[name](*args, **kwargs)
|
4

Indeed, getattr will get you there.

class X:
    def a(self):
        print('a called')
    def b(self, arg):
        print('b called with ' + arg)

x = X()
getattr(x, 'a')()
# a called
getattr(x, 'b')('foo')
# b called with foo

Just like getattr handles methods and fields the same way, you can handle functions and variables not associated with a class by referencing locals() or globals().

If you want to refer to a function in the global scope:

globals()['meth'](args)

For example:

def dispatch(name, *args, **kwargs):
  globals()[name](*args, **kwargs)

dispatch('meth3', 'hello', foo='bar')
# meth3: hello
# foo: bar

Remember in Python you can always pass a list of arguments or dict of keyword arguments using the **:

dispatch('meth3', *['hello'], **{'foo':'bar'})

If you truly prefer to pass arguments as list/dict to dispatch:

def dispatch(name, args, kwargs):
  globals()[name](*args, **kwargs)

dispatch('meth3', ['hello'], {'foo': 'bar'})

2 Comments

What if the methods are not in a class?
Then they are not methods but functions. I'll update the answer.

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.