Thursday, April 28, 2011

Calling unknown Python functions

This was the best name I could come up with for the topic and none of my searches yielded information relevant to the question.

How do I call a function from a string, i.e.

functions_to_call = ["func_1", "func_2", "func_3"]

for f in functions_to_call:
    call f
From stackoverflow
  • functions_to_call = ["func_1", "func_2", "func_3"]
    
    for f in functions_to_call:
        eval(f+'()')
    

    Edited to add:

    Yes, eval() generally is a bad idea, but this is what the OP was looking for.

    Teifion : This works perfectly, I'm not sure why it has so few upvotes.
    nosklo : -1 for not mentioning that the use of eval is a bad idea overall.
    bobince : indeed. There is no reason to use eval() here; `locals()[f]()` would have done fine. That's still a very questionable way of doing it, but way better than eval().
    gregturn : getattr works better using strings, which is ALSO what the OP was looking for. We shouldn't be spreading eval() when getattr() will do the job just as well.
    tgray : @nosklo, Does every answer really need to mention that eval is bad? It would probably suffice if you just picked apart the atrocity that is the eval function in your own answer.
    nosklo : @tgray: In my opinion, teaching a newbie to use eval like that is a bad thing, and should be downvoted.
    tgray : @nosklo, point taken... see comment on your answer for a follow-up question.
    Teifion : I'm well aware of the evils of eval, I came across it in PHP a few years ago. Thank you though for your concern.
    dF : +1 for ATFQ. Other answers have pointed out the problems with eval and suggested alternatives, but it's still worth knowing about it.
  • how do you not know the name of the function to call? Store the functions instead of the name:

    functions_to_call = [int, str, float]
    
    value = 33.5
    
    for function in functions_to_call:
        print "calling", function
        print "result:", function(value)
    
    Teifion : I could explain all about the exact program but I felt it'd be a waste of space and distract from the specific problem at hand.
    sykora : The functions are stored as strings in the question, not so in your answer, so the answer isn't exactly right...
    Aaron Maenpaa : @sykora ... but it does show a better way if you can twist the requirements of your problem a bit.
    nosklo : @Teifion: I think if you explained your exact problem instead of your failed attempt to solve it, you'd get better suggestions. Storing function names as strings and running them with eval/exec is a bad idea overall. There are always other better options.
    S.Lott : +1: Do not store the function names; store the functions themselves.
    tgray : @nosklo, would you mind going into more detail as to *why* eval is bad? Or link to a resource that explains it...
    nosklo : @tgray: Use of eval is slower, allows arbritary code execution, and shows a bad design. Maybe you should open a new top-level question about the issue.
  • See the eval and compile functions.

    This function can also be used to execute arbitrary code objects (such as those created by compile()). In this case pass a code object instead of a string. If the code object has been compiled with 'exec' as the kind argument, eval()‘s return value will be None.

    nosklo : -1 for not mentioning that the use of exec/eval is a bad idea overall.
  • Something like that...when i was looking at function pointers in python..

    def myfunc(x):
        print x
    
    dict = {
        "myfunc": myfunc
    }
    
    dict["myfunc"]("hello")
    
    func = dict.get("myfunc")
    if callable(func):
        func(10)
    
    nosklo : +1 dispatch dicts are a good way to do it if you really need to have a string
    Devin Jeanpierre : "function pointers"? No such thing.
    LB : yep...i know...I was looking for something equivalent...
    RaphaelSP : +1 for the idea (which also allows one to restrict the callable stuff to a specific set), but virtual -1 for calling the dictionary "dict", which will hide the type name in the local scope...
    LB : could you explain it to me a little more ? thanks :-)
    RaphaelSP : Every dictionary is an instance of the type "dict". It can be be build two ways: either using the syntax {}, or by calling the constructor "dict()". Calling a variable (or anything) "dict" prevents you from building a dictionary by calling "dict()", and from testing the type.
    RaphaelSP : Of course, this also applies to any other built-in type, and "dict" by itself is not that important. But still.
  • You can use the python builtin locals() to get local declarations, eg:

    def f():
        print "Hello, world"
    
    def g():
        print "Goodbye, world"
    
    for fname in ["f", "g"]:
        fn = locals()[fname]
        print "Calling %s" % (fname)
        fn()
    

    You can use the "imp" module to load functions from user-specified python files which gives you a bit more flexibility.

    Using locals() makes sure you can't call generic python, whereas with eval, you could end up with the user setting your string to something untoward like:

    f = 'open("/etc/passwd").readlines'
    print eval(f+"()")
    

    or similar and end up with your programming doing things you don't expect to be possible. Using similar tricks with locals() and dicts in general will just give attackers KeyErrors.

    Mark : you're a vicious jerk nosklo. he *did* explain it quite well
    nosklo : @Mark: indeed. I commented the wrong answer, sorry. Removed.
  • Have a look at the getattr function:

    http://docs.python.org/library/functions.html?highlight=getattr#getattr

    import sys
    
    functions_to_call = ["func_1", "func_2", "func_3"]
    
    for f in functions_to_call:
      getattr(sys.modules[__name__], f)()
    
  • Don't use eval! It's almost never required, functions in python are just attributes like everything else, and are accessible either using getattr on a class, or via locals():

    >>> print locals()
    {'__builtins__': <module '__builtin__' (built-in)>,
     '__doc__': None,
     '__name__': '__main__',
     'func_1': <function func_1 at 0x74bf0>,
     'func_2': <function func_2 at 0x74c30>,
     'func_3': <function func_3 at 0x74b70>,
    }
    

    Since that's a dictionary, you can get the functions via the dict-keys func_1, func_2 and func_3:

    >>> f1 = locals()['func_1']
    >>> f1
    <function func_1 at 0x74bf0>
    >>> f1()
    one
    

    So, the solution without resorting to eval:

    >>> def func_1():
    ...     print "one"
    ... 
    >>> def func_2():
    ...     print "two"
    ... 
    >>> def func_3():
    ...     print "three"
    ... 
    >>> functions_to_call = ["func_1", "func_2", "func_3"]
    >>> for fname in functions_to_call:
    ...     cur_func = locals()[fname]
    ...     cur_func()
    ... 
    one
    two
    three
    

0 comments:

Post a Comment

Note: Only a member of this blog may post a comment.