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

2408

如何使用函数名字符串调用函数?例如:

import foo
func_name = "bar"
call(foo, func_name)  # calls foo.bar()

8
使用eval可能会引起一些安全方面的担忧! - WMRamadan
24
FYI:通过动态名称访问字段、类和方法的语言特性称为反射。这可能会使未来的搜索更加容易。 - Nearoo
18个回答

2892

假设有一个名为 foo 的模块,其中包含方法 bar

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

getattr同样也可以用于类实例绑定的方法、模块级方法、类方法等,可谓是应用广泛。


21
hasattr或getattr可以用于确定函数是否已定义。我有一个数据库映射(eventType和handling functionName),我想确保我从未在我的Python中“忘记”定义事件处理程序。 - Shaun
15
如果您已经知道模块名称,这个方法就可以使用。但是,如果您希望用户以字符串形式提供模块名称,则此方法无法使用。 - Blairg23
13
如果需要避免 "NoneType is not callable" 异常,你也可以使用 getattr 的三参数形式:getattr(foo, 'bar', lambda: None)。对于格式不好的问题我很抱歉,似乎是 stackexchange 安卓应用的问题。 - geekofalltrades
7
如果您只关心本地/当前模块的函数,可以参考@sastanin提供的答案。 - NuSkooler
24
是的,如果你在 foo 模块中,可以使用 globals() 来实现这个功能:methodToCall = globals()['bar'] - Ben Hoyt
显示剩余12条评论

764
  • 使用locals()函数,该函数返回当前局部符号表的字典:

locals()["myfunction"]()
  • 使用 globals() 方法,可以获取全局符号表的字典:

  • globals()["myfunction"]()
    

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

    471

    基于Patrick的解决方案,为了动态获取模块,可以使用以下导入方式:

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

    104
    我不理解最后一条评论。import__有其自身的用途,上述文档中的下一句话是:“直接使用__import()的情况很少,除非您想要导入一个仅在运行时才知道名称的模块”。因此,对于给出的答案,点赞加一。 - hoffmaje
    65
    请使用importlib.import_module。官方文档对于__import__说:“这是一个高级函数,不像importlib.import_module()一样在日常Python编程中需要使用。”http://docs.python.org/2/library/functions.html#__import__ - glarrain
    10
    只要你可以接受仅支持2.7及以上版本,就没有问题。 - Xiong Chiamiov
    4
    importlib.import_module 在 Python 3.6 中是被支持的。请看 https://docs.python.org/3.6/library/importlib.html#importlib.import_module。 - cowlinator
    10
    是的,3.6版本属于“2.7及以上”范畴,无论是在版本语义上还是发布日期上都是如此(它是六年后出现的)。此外,在我发表评论的三年后,它也不存在。 ;) 在3.x分支中,该模块自3.1版本就已经存在了。2.7和3.1现在已经相当古老了。你仍然会发现一些只支持2.6的服务器,但现在使用importlib作为标准建议可能更值得考虑。 - Xiong Chiamiov
    对于使用点格式的模块路径,例如 foo.bar.module__input__ 仅导入基本模块 foo,而 importlib.import_module 返回 foo.bar.module。使用 foo/bar/module__import__ 返回预期的模块。 - Conner M.

    164

    仅做一点简单的贡献。如果我们需要实例化的类与当前文件相同,可以使用以下代码:

    # 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')
    

    2
    如果你在类函数内调用这个函数会怎样? - Jürgen K.

    158

    给定一个字符串,其中包含完整的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()
    

    4
    这对我很有帮助。它是__import__函数的轻量级版本。 - Pankaj Bhambhani
    5
    我认为这是最好的答案。 - SdSaati

    88

    根据Python编程FAQ,最佳答案是:

    functions = {'myfoo': foo.bar}
    
    mystring = 'myfoo'
    if mystring in functions:
        functions[mystring]()
    

    这种技术的主要优点是字符串不需要与函数名称匹配。这也是模拟 case 结构的主要技术。

    72

    希望没有人需要的答案

    类似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")()
    

    26
    可以通过递归扫描目录树并自动挂载USB驱动器来改进这个功能。 - pevinkinel
    1
    这绝对是我想要的答案。完美。 - ArjunSahlot

    46

    试试这个。虽然这仍然使用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)
    

    3
    这将是有风险的。字符串可以包含任何内容,而eval会毫无考虑地对其进行求值。 - iankit
    7
    当然,你必须注意使用环境,考虑到风险因素,判断该用法是否合适。 - tvt173
    6
    函数不应该负责验证它的参数 - 这是另一个函数的工作。说使用 eval 处理字符串存在风险,就好像说每个函数的使用都是有风险的。 - red777
    3
    除非绝对必要,否则不应使用eval。在这种情况下,getattr(__module__, method_name)是更好的选择。 - moi

    43

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

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

    2
    稍微通用一些的写法是 handler = getattr(sys.modules[__name__], myFnName) - lony
    1
    如果函数是类函数,它是如何工作的? - Jürgen K.

    21

    作为这个问题的重复,如何使用方法名称分配调用类中的方法,我在这里发布一个相关的答案:

    情况是,在类中的一个方法想要动态地调用同一类中的另一个方法,我已经添加了一些细节到原始示例,提供了一些更广泛的场景和清晰度:

    class MyClass:
        def __init__(self, i):
            self.i = i
    
        def get(self):
            func = getattr(MyClass, 'function{}'.format(self.i))
            func(self, 12)   # This one will work
            # self.func(12)    # But this does NOT work.
    
    
        def function1(self, p1):
            print('function1: {}'.format(p1))
            # do other stuff
    
        def function2(self, p1):
            print('function2: {}'.format(p1))
            # do other stuff
    
    
    if __name__ == "__main__":
        class1 = MyClass(1)
        class1.get()
        class2 = MyClass(2)
        class2.get()
    

    输出(Python 3.7.x)

    function1:12

    function2:12


    1
    非常好的答案,谢谢 :) 我也尝试了同样的方法,但是失败了,因为我不知道在实际函数调用中必须再次包含“self”。您能解释一下为什么这是必要的吗? - M M
    1
    我的最佳猜测是:在Python中,obj.method实际上调用的是method(self, ...),在getattr的情况下,Python解释器无法应用这种合成糖。 - Serjik
    2
    最佳答案:重点是在getattr命令中使用类名而不是“self”。我试图像这样使用getattr(self,key)()但它会出现错误,例如int不可调用,但当我将其更改为“getattr(HomeScreen,key)(self)”时,它现在可以工作了。“HomeScreen”是类名,顺便说一下。谢谢... - drascom

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