【问题标题】:Choosing the Function Dynamically动态选择函数
【发布时间】:2013-01-22 20:59:53
【问题描述】:

我有一些如下所示的代码:

if command == 'a':
    do_a(a, b, c)
elif command == 'b':
    do_b(a, b, c)
elif command == 'c':
    do_c(a, b, c)

我怎样才能用更优雅的东西代替这种类型的东西?也许,类似于 do_[command](a, b, c) 的东西,被调用的函数取决于命令?

有可能吗?

【问题讨论】:

  • 你可能想了解更多关于 Python 闭包 getpython3.com/diveintopython3/generators.htmllambda 和 Python eval docs.python.org/3.1/library/functions.html#eval (但不要滥用 eval
  • 类似eval('do_'+command)(a,b,c)
  • 听起来像是一个函数字典的工作。尽可能避免eval
  • 不要为此使用eval。 Eval 对于代码生成(动态定义函数/类)很有用,例如 namedtuple() 实现,在这种情况下,使用低级 API 构建类会不必要地复杂并模糊代码的意图。在这里,您没有动态定义任何内容,eval 会使意图变得不那么清晰。

标签: python


【解决方案1】:

您可以使用“反射”执行类似的操作,通过字符串名称调用函数,如下所述:

Python: call a function from string name

但是

如果您不能绝对控制传递给 command 的内容,那将不会更优雅,可读性也会降低,并且更容易搞砸。

你的版本很好:

显式优于隐式。

简单胜于复杂。

如果您真的想避免 elif's 我会采用 cmets 中建议的 dict 函数方法。

这样的事情远离 eval。

【讨论】:

    【解决方案2】:

    您可以将命令存储在字典中并在需要时进行查找:

    In [15]: commands = {'mul': lambda x,y: x*y,
                         'add': lambda x,y: x+y}
    
    In [16]: commands['mul'](3,4)
    Out[16]: 12
    
    In [17]: commands['add'](3,4)
    Out[17]: 7
    
    In [18]: command = 'add'; vars = (4,5)
    In [19]: commands[command](*vars)
    Out[19]: 9
    

    您应该检查command 是否确实在commands 中:

    if command in commands:
        commands[command]()
    else:
        # handle error
    

    【讨论】:

      【解决方案3】:

      我正在等待其他人提供更好的解决方案,但是:

      def dispatch(cmd, *args):
         globals()['do_' + cmd](*args)
      
      def do_a(a, b, c): 
      

      用法:

      dispatch('a', 1, 2, 3)
      

      显然不健壮,但dispatch 可以确保该函数存在。

      可能一个不错的方法是使用装饰器。

      @command()
      def do_a(a,b,c):
          ...
      
      @command("do_x")
      def myfunc(a, b, c):
         ...
      

      您的装饰器可以维护调度查找表等。

      这取决于您对可能需要使用现有函数或担心名称冲突的担忧程度。

      【讨论】:

        【解决方案4】:

        这可能会对您有所帮助,这里的源可以是任何以函数名称结尾的通用模块路径,例如 模块.funcname

        所以示例调用看起来像 convertStringToFunction(module.funcname)

        def import_module(name):
            mod = __import__(name)
            components = name.split('.')
            for comp in components[1:]:
                mod = getattr(mod, comp)
        return mod
        
        def convertStringToFunction(source):
            tempArr = source.rsplit('.',1)
            mod = import_module(tempArr[0])
            func = getattr(mod, tempArr[1])
            return func
        

        【讨论】:

          【解决方案5】:
          def do(fname):
              return {'a':do_a, 'b':do_b, 'c':do_c}.get(fname)
          
          def do_a(x,y,z): return x + y + z
          def do_b(x,y,z): pass
          def do_c(x,y,z): pass
          

          用法:

          do('a')(1,2,3)
          6
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2012-09-08
            • 1970-01-01
            • 2022-01-18
            • 1970-01-01
            • 1970-01-01
            • 2011-12-24
            • 1970-01-01
            相关资源
            最近更新 更多