在函数中将函数名作为参数传递

23

我试图将一个函数的名称作为参数传递给另一个函数,但是出现了错误:TypeError: 'str' object is not callable。以下是问题的简化示例:

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

当我这样调用 doIt:

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

我得到:

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

1
加油!globals()函数起作用了!非常感谢大家的帮助。 - Desmond
13
不要!听听Alex Martelli的意见!他写了《Python速览》!不要采用丑陋的黑科技来实现你可以通过清晰代码实现的任务!学会正确地使用这门语言! - jdd
我认为这里有你问题的答案: 进入链接描述 - Łukasz Hawer
9个回答

30
如果你想传递函数的名称,正如你所说并且你正在做的,当然你不能调用它--为什么要"调用一个名称"呢?这是没有意义的。如果你想调用它,传递函数本身,也就是最强调的不要传递函数名称。
var = 'dork1'

但是更确切的说

var = dork1

不带引号!

编辑: 在评论中,原作者想知道如何通过函数名(作为字符串)获取函数对象。恰巧我在刚从 OSCON 上教授的教程中展示了如何做到这一点 - 从 这里 获取幻灯片,并查看第47页,“延迟加载回调”部分:

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)

所以你可以传递 LazyCallable('somemodule.dork1') 并且一路愉快。如果当然你不需要处理模块(那一定是很奇怪的架构!-),调整这段代码就很容易了。


3
Desmond可能习惯于“php风格”的函数名传递方式,即将函数名放在一个字符串中,然后传递和调用它,在那里它可能被执行或类似的操作。打颤 - jdd

7

不要传递函数的名称。

传递函数本身。

fun = dork1
ned = doIt(3, fun, 4, 9)
print (ned)

7

我很高兴找到了这个问题,但不知道是否已有答案。我的解决方案如下:

def doIt(func, *args):
   func_dict = {'dork1':dork1,'dork2':dork2}
   result = func_dict.get(func)(*args)
   return result

def dork1(var1, var2, var3):
   thing = (float(var1 + var2) / var3)
   return thing

def dork2(var1, var2, var3):
   thing = float(var1) + (float(var2) / var3)
   return thing

这可以按以下方式运行:

func = 'dork2'
ned = doIt(func,3, 4, 9)
print ned

1
不错的解决方法,但这并不能作为 OP 问题的通用解决方案。 - Sid Sahay

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

在这个例子中,var是一个字符串。函数doIt会“调用”它的第二个参数(你需要传递var)。可以考虑传递一个函数代替。

如果我从一个存储为字符串的字典值中获取dork1,该怎么办?如何将其转换为函数“对象”(我知道这里不是正确的术语),而不是字符串,以便我可以正确地传递它? - Desmond
请查看eval()函数:http://docs.python.org/library/functions.html#eval - advait
1
你的字典值应该只是函数对象:funcs = {'first': dork1, 'second': dork2'}除非你确定需要它,否则不要使用eval函数。即使如此,你可能仍然不需要它。几乎肯定有更好的解决方案。 - jdd
2
@jeremiahd 是正确的 - 我提出的名称到函数转换的一个好理由是为了避免加载许多模块来设置回调系统(如果大多数回调在给定运行中可能不需要,则可能会减慢启动速度而没有任何目的 - 惰性加载将“分摊负载”超出启动时间,并且只让您“按需付费”,在某些系统中这是一个很好的权衡)。 - Alex Martelli

2

您可能不应该这样做,但您可以使用eval()函数获取该函数。

例如,要使用len函数:

eval("len")(["list that len is called on"])

0

在Python中,函数是一等对象。做这个:

var = dork1

如果您必须传递字符串,例如用户输入,则应:
globals()[var]

将查找函数对象。


0
def run_function(function_name):
    function_name()

在这个例子中,调用run_function(any_function)将会调用any_function()

如何检查函数是否存在? - Huy Vo
如果函数不存在,则函数调用会抛出NameErrorAttributeError异常。只需捕获这些异常即可。 - Zenadix

0
class renderAttributes():
    def __init__(self, name):
        self.name = name
    def decorator_function(self, func_name):
        func_dict = {'my_function': self.my_function}
        def wrapper_function():
            result = func_dict.get(func_name)()
            return result
        return wrapper_function
    def my_function(self):
        //Do whatever you want to do
        return self.name

然后我可以创建一个renderAttributes类的对象,并通过简单地传递函数名来调用类内定义的任何方法,如下所示

render_attr_obj = renderAttributes('Bob')
return_function = render_attr_obj.decorator_function('my_function')
return_functon() 

return_function = renderAttributes('Rob')
return_function = render_attr_obj.decorator_function('my_function')
return_functon() 

0

如果这些函数是类的一部分,那么getattr可能会起作用。

class SomeClass():
    def doIt(self, a, func, y, z):
        result = z
        result = getattr(self, func)(a, y, result)
        return result

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

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

当我尝试从sys.argv传递函数名称时,这对我很有效。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接