85

I have a function name stored in a variable like this:

myvar = 'mypackage.mymodule.myfunction'

and I now want to call myfunction like this

myvar(parameter1, parameter2)

What's the easiest way to achieve this?

schneck
  • 10,556
  • 11
  • 49
  • 74
  • 4
    Why not store the function itself? `myvar = mypackage.mymodule.myfunction` is much cleaner. – ironfroggy Feb 17 '10 at 18:54
  • 1
    From a comment below: «It must be a string because at the place where it is defined, the application does not know the desired function, since it's a generic app.» – schneck – badp Feb 17 '10 at 19:35

8 Answers8

128
funcdict = {
  'mypackage.mymodule.myfunction': mypackage.mymodule.myfunction,
    ....
}

funcdict[myvar](parameter1, parameter2)
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
46

It's much nicer to be able to just store the function itself, since they're first-class objects in python.

import mypackage

myfunc = mypackage.mymodule.myfunction
myfunc(parameter1, parameter2)

But, if you have to import the package dynamically, then you can achieve this through:

mypackage = __import__('mypackage')
mymodule = getattr(mypackage, 'mymodule')
myfunction = getattr(mymodule, 'myfunction')

myfunction(parameter1, parameter2)

Bear in mind however, that all of that work applies to whatever scope you're currently in. If you don't persist them somehow, you can't count on them staying around if you leave the local scope.

18
def f(a,b):
    return a+b

xx = 'f'
print eval('%s(%s,%s)'%(xx,2,3))

OUTPUT

 5
Pratik Deoghare
  • 35,497
  • 30
  • 100
  • 146
12

Easiest

eval(myvar)(parameter1, parameter2)

You don't have a function "pointer". You have a function "name".

While this works well, you will have a large number of folks telling you it's "insecure" or a "security risk".

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 2
    "insecure": If myvar comes from user input, yes :) – Federico A. Ramponi Feb 17 '10 at 18:12
  • Aside from security issues, I get a "NameError: name 'mypackage' is not defined", while I can import modules from there going the "normal" way. And: of course, the string that shall be evaluated is fixed and not changed by the user. – schneck Feb 17 '10 at 18:13
  • 1
    @schneck, then why would it possibly have to be a string? – Mike Graham Feb 17 '10 at 18:17
  • 2
    @schneck: If eval('the string') does not produce the correct function, then your question is incomplete. You've omitted something crucial. You might try posting something that *does* work along with the detailed error message of what doesn't work. – S.Lott Feb 17 '10 at 18:19
  • 2
    @Derrick Turn, @Truptych: They'd be right only if the string came from a malicious sociopath. User input from unauthenticated people on the internet is likely to involve malicious sociopaths. Most everything else does not generally involve malicious sociopaths, reducing the security risk to exactly the same risk as someone deleting all the source code for the application. – S.Lott Feb 17 '10 at 18:20
  • It's only insecure if the data comes from external sources. If myvar is defined in your app, it's no less secure that directly calling the function. – Bryan Oakley Feb 17 '10 at 18:22
  • @Mike Graham: It must be a string because at the place where it is defined, the application does not know the desired function, since it's a generic app. – schneck Feb 17 '10 at 18:26
  • 6
    @schneck, I don't understand what you mean by "since it's a generic app" could mean here. If you have defined this as a string literal, you already know enough about it that you don't need to do so. – Mike Graham Feb 17 '10 at 18:31
  • 1
    Given the ability to run any functions given in `myvar`, why worry about `eval`? – kennytm Feb 17 '10 at 19:32
  • @Mike Graham: I thought about that and I think you're right; I should reference a real function name rather than by a string. – schneck Feb 17 '10 at 20:29
7

Why not store the function itself? myvar = mypackage.mymodule.myfunction is much cleaner.

ironfroggy
  • 7,991
  • 7
  • 33
  • 44
4
modname, funcname = myvar.rsplit('.', 1)
getattr(sys.modules[modname], funcname)(parameter1, parameter2)
Matt Anderson
  • 19,311
  • 11
  • 41
  • 57
3

eval(compile(myvar,'<str>','eval'))(myargs)

compile(...,'eval') allows only a single statement, so that there can't be arbitrary commands after a call, or there will be a SyntaxError. Then a tiny bit of validation can at least constrain the expression to something in your power, like testing for 'mypackage' to start.

SilverbackNet
  • 2,076
  • 17
  • 29
2

I ran into a similar problem while creating a library to handle authentication. I want the app owner using my library to be able to register a callback with the library for checking authorization against LDAP groups the authenticated person is in. The configuration is getting passed in as a config.py file that gets imported and contains a dict with all the config parameters.

I got this to work:

>>> class MyClass(object):
...     def target_func(self):
...         print "made it!"
...    
...     def __init__(self,config):
...         self.config = config
...         self.config['funcname'] = getattr(self,self.config['funcname'])
...         self.config['funcname']()
... 
>>> instance = MyClass({'funcname':'target_func'})
made it!

Is there a pythonic-er way to do this?

Rob Fagen
  • 774
  • 1
  • 8
  • 24