views:

247

answers:

4

This is for use in a JSON API. I don't want to have:

if method_str == 'method_1':
    method_1()

if method_str == 'method_2':
    method_2()

For obvious reasons this is not optimal. How would I use map strings to methods like this in a reusable way (also note that I need to pass in arguments to the called functions).

Here is an example:

INCOMING JSON:

{
    'method': 'say_something',
    'args': [
        135487,
        'a_465cc1'
    ]
    'kwargs': {
        'message': 'Hello World',
        'volume': 'Loud'
    }
}

# JSON would be turned into Python with Python's built in json module.

Resulting call:

# Either this
say_something(135487, 'a_465cc1', message='Hello World', volume='Loud')

# Or this (this is more preferable of course)
say_something(*args, **kwargs)
+1  A: 

Assuming the functions are all global variables (they are, unless they were defined inside another functions), they can be accessed with the globals() function. globals() returns a dictionary of all global variables, including functions.

For example:

$ python
Python 2.6.2 (r262:71600, Apr 16 2009, 09:17:39) 
[GCC 4.0.1 (Apple Computer, Inc. build 5250)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def some_function():
...     print "Hello World!"
... 
>>> globals()
{'__builtins__': <module '__builtin__' (built-in)>, '__name__': '__main__', '__doc__': None, 'some_function': <function some_function at 0x6326b0>, '__package__': None}
>>> globals()['some_function']()
Hello World!
Jeffrey Aylesworth
For the given example, this could be really insecure - there is no guarantee the INCOMING_JSON is from a friendly source. Either incorporate a function whitelist, or use Mike's solution.
spookylukey
+17  A: 

For methods of instances, use getattr

>>> class MyClass(object):
...  def sayhello(self):
...   print "Hello World!"
... 
>>> m=MyClass()
>>> getattr(m,"sayhello")()
Hello World!
>>> 

For functions you can look in the global dict

>>> def sayhello():
...  print "Hello World!"
... 
>>> globals().get("sayhello")()
Hello World!

In this case, since there is no function called prove_riemann_hypothesis the default function (sayhello) is used

>>> globals().get("prove_riemann_hypothesis", sayhello)()
Hello World!

The problem with this approach is that you are sharing the namespace with whatever else is in there. You might want to guard against the json calling methods it is not supposed to. A good way to do this is to decorate your functions like this

>>> json_functions={}
>>> def make_available_to_json(f):
...  json_functions[f.__name__]=f
...  return f
...
>>> @make_available_to_json
... def sayhello():
...  print "Hello World!"
...
>>> json_functions.get("sayhello")()
Hello World!
>>> json_functions["sayhello"]()
Hello World!
>>> json_functions.get("prove_riemann_hypothesis", sayhello)()
Hello World!
gnibbler
Your final solution seems to be magically calling the function. Shouldn't that be `json_functions.get("sayhello")()` or better `json_functions.get["sayhello"]()`? Is that from an actual interpreter session?
Mike Graham
@Mike, some of it is :)
gnibbler
@gnibbler, why still prefer the `get` method to `[]`? The latter is the natural syntax and, if `"sayhello"` isn't valid, throws a more meaningful exception.
Mike Graham
@Mike, I find I use get more often because I want to supply the default argument. If your use case prefers an exception, use `[]` for sure.
gnibbler
@gnibbler, yours would raise an exception too, just a much less useful one than doing it the normal way.
Mike Graham
@Mike, Yeah, in this case when no default function is supplied it would indeed be silly to try to call None
gnibbler
+6  A: 

Use getattr. For example:

class Test(object):
    def say_hello(self):
        print 'Hell no, world!!111'
    def test(self):
        getattr(self, 'say_hello')()
myfreeweb
what about `{'method': '__init__',...`
gnibbler
+6  A: 

The clean, safe way to do this is to make a dict mapping names to functions. If these are actually methods, the best way is still to make such a dict, though getattr is also available. Using globals or eval is unsafe and dirty.

Mike Graham