stackoverflow about python dynameic inovke function with it's name

up vote 3 down vote favorite
 

I am trying to pass the name of a function into another function as an argument but I get an error: "TypeError: 'str' object is not callable". Here is a simplified example of the problem:

def doIt(a, func, y, z):
    result
= z
    result
= func(a, y, result)
   
return result

def dork1(arg1, arg2, arg3):
    thing
= (arg1 + arg2) / arg3
   
return thing

def dork2(arg1, arg2, arg3):
    thing
= arg1 + (arg2 / arg3)
   
return thing

When I call doIt like so:

var = 'dork1'
ned
= doIt(3, var, 4, 9)
print (ned)

I get:

Traceback (most recent call last):
   
File "<pyshell#9>", line 1, in <module>
     ned
= doIt(3, var, 4, 9)
   
File "<pyshell#2>", line 3, in doIt
     result
= func(a, y, result)
TypeError: 'str' object is not callable
link|flag
 
 
 
Rock on! globals() worked! Thank you all for your help. – Desmond Jul 28 at 1:06
4
 
Noooo! Listen to Alex Martelli! He WROTE python in a nutshell! Don't resort to using an ugly hack that is rarely if ever needed to do something you can do cleanly! Learn to use the language correctly! – jeremiahd Jul 28 at 1:14
 
add comment
up vote 5 down vote

If you want to pass the function's name, as you said and you're doing, of course you can't call it -- why would one "call a name"? It's meaningless.

If you want to call it, pass the function itself, that is, most emphatically not

var = 'dork1'

but rather

var = dork1

without quotes!

Edit: the OP wonders in a comment (!) how to get a function object given the function name (as a string). As it happens I just showed how to do that in a tutorial I taught at OSCON (from which I'm just back) -- get the slides from here and see page 47, "Lazy-loading callbacks":

class LazyCallable(object):
 
def __init__(self, name):
   
self.n, self.f = name, None
 
def __call__(self, *a, **k):
   
if self.f is None:
      modn
, funcn = self.n.rsplit('.', 1)
     
if modn not in sys.modules:
        __import__
(modn)
     
self.f = getattr(sys.modules[modn],
                       funcn
)
   
self.f(*a, **k)

So you could pass LazyCallable('somemodule.dork1') and live happily ever after. If you don't need to deal with the module of course (what a weird architecture that must imply!-) it's easy to adjust this code.

link|flag
 
 
 
Desmond might be used to "php-style" function-name passing, which is putting a function name in a string and then passing that around and calling it, where it presumably get's evaled or something. shudder. – jeremiahd Jul 28 at 1:05
 
add comment
up vote 2 down vote

Don't pass the name of a function.

Pass the function.

fun = dork1
ned
= doIt(3, fun, 4, 9)
print (ned)
link|flag
 
  
 
add comment
up vote 1 down vote
var = 'dork1'
ned
= doIt(3, var, 4, 9)
print (ned)

In this example, var is a string. The doIt function "calls" its second argument (for which you pass var). Pass a function instead.

link|flag
 
 
 
What if I get dork1 from a dictionary value which is stored as a string? How do I convert it to a function "object" (I know that isn't the correct terminology here) rather than a string so I can pass it in correctly? – Desmond Jul 28 at 1:02
 
 
Check out the eval() function: docs.python.org/library/functions.html#eval – advait Jul 28 at 1:03
1
 
Your dict value should just be the function object: funcs = {'first': dork1, 'second': dork2'} do not check out the eval function, unless you are sure you need it. and even then, you probably still don't need it. There is almost certainly a better solution. – jeremiahd Jul 28 at 1:06
2
 
@jeremiahd is right -- the one good reason for the name-to-function translation which I present is to avoid loading many, many modules in order to set up a system of callbacks (it might slow down startup for no purpose if most of the callbacks may be unneeded in a given run -- lazy loading will "spread the load" beyond just startup time, and only make you "pay for what you use", which in some systems is a good tradeoff). – Alex Martelli Jul 28 at 1:34
 
add comment
up vote 0 down vote

If you have to pass it as a string you can call it with:

globals()['dork1'](arg1,arg2,arg3)
link|flag
 
  
 
add comment
up vote -1 down vote

Functions are first-class objects in python. Do this:

var = dork1

If you must pass a string, such as user input, then:

globals()[var]

will look up the function object.

link|flag
 
  
 
add comment
up vote 0 down vote

You probably shouldn't do this, but you can get the function using eval()

for example, to use len,

eval("len")(["list that len is called on"])
link|flag
 
  
 
add comment

Your Answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.

原文地址:https://www.cnblogs.com/lexus/p/1844403.html