题 使用其名称(字符串)调用模块的函数


在Python程序中给定带有函数名称的字符串调用函数的最佳方法是什么。例如,假设我有一个模块 foo,我有一个内容为的字符串 "bar"。什么是打电话的最佳方式 foo.bar()

我需要获取函数的返回值,这就是我不仅仅使用它的原因 eval。我想通过使用如何做到这一点 eval 定义一个返回该函数调用结果的临时函数,但我希望有一种更优雅的方法来执行此操作。


1189
2017-08-06 03:36


起源




答案:


假设模块 foo 用方法 bar

import foo
method_to_call = getattr(foo, 'bar')
result = method_to_call()

就此而言,第2行和第3行可以压缩为:

result = getattr(foo, 'bar')()

如果这对你的用例更有意义。您可以使用 getattr 以这种方式对类实例绑定方法,模块级方法,类方法......列表继续。


1443
2017-08-06 03:57



hasattr或getattr可用于确定是否定义了函数。我有一个数据库映射(eventType和处理functionName),我想确保我从来没有“忘记”在我的python中定义一个事件处理程序 - Shaun
如果您已经知道模块名称,则此方法有效。但是,如果您希望用户将模块名称作为字符串提供,则不起作用。 - Blairg23
如果你需要避免NoneType不可调用的异常,你也可以使用getattr的三参数形式:getattr(foo,'bar',lambda:None)。我为格式化道歉; stackexchange android应用程序显然很糟糕。 - geekofalltrades
如果您只关心本地/当前模块的功能,请参阅@sastanin提供的答案。 - NuSkooler
注意:很酷的+1,这让我再次理解,在Python中,一切都是对象。因此,它也适用于变量,您可以像访问任何其他对象的变量一样访问模块的变量。 - tuned


locals()["myfunction"]()

要么

globals()["myfunction"]()

当地人 返回包含当前本地符号表的字典。 全局 返回带有全局符号表的字典。


401
2018-05-07 12:45



如果您需要调用的方法在您调用的同一模块中定义,则使用globals / locals的此方法很好。 - Joelmob
@Joelmob有没有其他方法可以通过字符串从根命名空间中获取对象? - Nick T
@NickT我只知道这些方法,我不认为还有其他任何功能与这些方法相同,至少我想不出应该有更多的原因。 - Joelmob
我有理由(实际上是什么让我在这里):模块A有一个函数F需要按名称调用函数。模块B导入模块A,并调用函数F,调用函数G,这在模块B中定义。此调用失败,因为,显然,函数F只运行模块F中定义的全局变量 - 所以全局() ['G'] =无。 - David Stein


帕特里克的解决方案可能是最干净的。 如果您还需要动态选择模块,可以将其导入为:

module = __import__('foo')
func = getattr(module, 'bar')
func()

243
2017-08-07 11:35



我不明白最后的评论。 __import__有自己的权利,上述文档中的下一句话说:“直接使用__import __()是罕见的,除非您要导入名称仅在运行时已知的模块”。所以:给定答案+1。 - hoffmaje
使用 importlib.import_module。官方文件说 __import__:“这是日常Python编程中不需要的高级函数,与importlib.import_module()不同。” docs.python.org/2/library/functions.html#__import__ - glarrain
@glarrain只要你支持2.7及以上就可以了。 - Xiong Chiamiov
@Xiong Chaimiov, importlib.import_module 在3.6中受支持。看到 docs.python.org/3.6/library/... - cowlinator
@cowlinator是的,3.6是“2.7及以上”的一部分,无论是在严格的版本控制语义还是发布日期(大约六年后)。在我的评论之后三年内它也不存在。 ;)在3.x分支中,模块自3.1起已经存在。 2.7和3.1现在相当古老;你仍然会发现只支持2.6的服务器,但是现在可能值得将importlib作为标准建议。 - Xiong Chiamiov


只是一个简单的贡献。如果我们需要实例的类在同一个文件中,我们可以使用这样的东西:

# Get class from globals and create an instance
m = globals()['our_class']()

# Get the function (from the instance) that we need to call
func = getattr(m, 'function_name')

# Call it
func()

例如:

class A:
    def __init__(self):
        pass

    def sampleFunc(self, arg):
        print('you called sampleFunc({})'.format(arg))

m = globals()['A']()
func = getattr(m, 'sampleFunc')
func('sample arg')

# Sample, all on one line
getattr(globals()['A'](), 'sampleFunc')('sample arg')

而且,如果不是一个班级:

def sampleFunc(arg):
    print('you called sampleFunc({})'.format(arg))

globals()['sampleFunc']('sample arg')

81
2017-08-19 09:40





给定一个字符串,带有函数的完整python路径,这就是我如何获得所述函数的结果:

import importlib
function_string = 'mypackage.mymodule.myfunc'
mod_name, func_name = function_string.rsplit('.',1)
mod = importlib.import_module(mod_name)
func = getattr(mod, func_name)
result = func()

65
2017-10-16 00:24



这对我有所帮助。它的轻量级版本 __import__ 功能。 - Pankaj Bhambhani
整洁的解决方案伴侣! - Nebulosar


答案(我希望)没有人想过

Eval喜欢的行为

getattr(locals().get("foo") or globals().get("foo"), "bar")()

为什么不添加自动导入

getattr(
    locals().get("foo") or 
    globals().get("foo") or
    __import__("foo"), 
"bar")()

如果我们有额外的词典,我们想检查

getattr(next((x for x in (f("foo") for f in 
                          [locals().get, globals().get, 
                           self.__dict__.get, __import__]) 
              if x)),
"bar")()

我们需要更深入

getattr(next((x for x in (f("foo") for f in 
              ([locals().get, globals().get, self.__dict__.get] +
               [d.get for d in (list(dd.values()) for dd in 
                                [locals(),globals(),self.__dict__]
                                if isinstance(dd,dict))
                if isinstance(d,dict)] + 
               [__import__])) 
        if x)),
"bar")()

31
2018-04-09 10:17





根据的最佳答案 Python编程FAQ 将会:

functions = {'myfoo': foo.bar}

mystring = 'myfoo'
if mystring in functions:
    functions[mystring]()

这种技术的主要优点是字符串不需要匹配函数的名称。这也是用于模拟案例构造的主要技术


28
2017-10-24 13:20





对于它的价值,如果你需要将函数(或类)名称和应用程序名称作为字符串传递,那么你可以这样做:

myFnName  = "MyFn"
myAppName = "MyApp"
app = sys.modules[myAppName]
fn  = getattr(app,myFnName)

18
2018-02-14 05:55



只是更通用的是 handler = getattr(sys.modules[__name__], myFnName) - lony


尝试这个。虽然这仍然使用eval,但它只使用它 从当前上下文召唤函数。然后,您可以根据需要使用真正的功能。

对我来说,主要的好处是你会在召唤函数时得到任何与eval相关的错误。然后你会得到 只要 你打电话时与功能有关的错误。

def say_hello(name):
    print 'Hello {}!'.format(name)

# get the function by name
method_name = 'say_hello'
method = eval(method_name)

# call it like a regular function later
args = ['friend']
kwargs = {}
method(*args, **kwargs)

15
2017-12-07 18:29



这将是有风险的。字符串可以有任何东西,而eval最终会在没有任何考虑的情况下对其进行评估。 - iankit
当然,考虑到这些风险,你必须注意你使用它的背景,这是否合适。 - tvt173


所有建议都没有帮助我。我确实发现了这一点。

<object>.__getattribute__(<string name>)(<params>)

我使用的是python 2.66

希望这可以帮助


14
2017-12-28 16:56



在哪方面这比getattr()更好? - V13
正是我想要的。奇迹般有效!完善!! self.__getattribute__('title') 等于 self.title - ioaniatr