【问题标题】:How to get the function names of functions in a separate file?如何在单独的文件中获取函数的函数名称?
【发布时间】:2018-12-21 01:43:17
【问题描述】:

我正在创建一个聊天机器人,它有 2 个主要文件,main.py 运行机器人,commands.py 包含机器人识别的命令。我试图从commands.py 获取函数名称,以便在main.py 中使用该信息(作为简单的字符串/存储为变量)。例如:

commands.py

def add():
    pass

def delete():
    pass

def change():
    pass

基本上,我希望能够存储在变量commands = ['add', 'delete', 'change'] 或类似的东西中。我对装饰器没有任何经验,但这可能是使用它的好时机吗?为了能够使用装饰器注册命令?我愿意接受任何建议,谢谢!

【问题讨论】:

    标签: python python-3.x decorator


    【解决方案1】:

    IIUC,

    import commands
    
    >>> dir(commands)
    ['__builtins__',
     '__cached__',
     '__doc__',
     '__file__',
     '__loader__',
     '__name__',
     '__package__',
     '__spec__',
     'add',
     'delete',
     'change']
    

    然后过滤掉魔法属性

    >>> function_names = [func for func in dir(commands) if not func.startswith('__')]
    
    ['add', 'delete', 'change']
    

    【讨论】:

    • 定义__all__ 不是更直接地实现相同的想法吗?
    • 为什么要直截了当?如果你有 200 种方法怎么办?你会在 __all__ 中硬编码 200 个字符串?
    • 然后我会使用我建议的基于类的方法。直截了当,因为这是__all__ 的预期目的,而通过模块dir() 进行搜索是不寻常的。目前尚不清楚列表理解的意图是什么,这将出现在 OP 的代码中。
    • 我的解决方案中唯一的缺点是如果您在您的文件中有属性。然后,var 名称将与函数名称一起显示。
    • 属性,或者如果你导入其他任何东西,或者辅助函数。这看起来很简单,直到您尝试维护一个相当大的程序(如您所说,200 个命令)并在您的动态命令数组中意外地以垃圾结尾。
    【解决方案2】:

    我最近就是这样做的:

    def is_command(class_method):
        class_method.is_command = True
        return classmethod(class_method)
    

    然后将你的命令分组到一个类中:

    class CommandsModule:
        @is_command
        def add(cls):
            pass
    

    装饰器不是绝对必要的,但可以将您的实际命令与其他不应被界面“调用”的辅助函数巧妙地分开。为了获得命令,我有一个函数(在我的例子中是另一个类):

    def get_command(self, command_word):
        if not command_word:
            return
    
        command_func = getattr(CommandsModule, str(command_word).lower(), None)
        if (not callable(command_func) or
                not getattr(command_func, 'is_command', False)):
            return
    
        return command_func
    

    从这里可以很容易地添加额外的功能,例如更好地检查命令或带有参数的命令。添加新命令就像编写 CommandsModule 方法并放置装饰器以表明它是可调用的一样简单。

    【讨论】:

    • 感谢您的回复,如果您不介意,我很想知道您最终是如何使用它的。即您是否只是使用它来验证命令是否有效/可调用?或者你然后用它来调用命令?谢谢!
    • 我两者都用过。我有一个call_command 函数,它接受用户提供的命令(来自聊天)和可选的任何参数。 call_command 使用提供的名称调用 get_command。如果无效,get_command 返回None。否则,您将获得正确的函数,准备调用。 call_command 的其余部分用于处理命令参数和错误检查。
    【解决方案3】:

    您不必使用装饰器。相反,请使用getattr:

    import commands #the file with bot operations
    val1 = getattr(commands, 'add')()
    val2 = getattr(commands, 'delete')()
    val3 = getattr(commands, 'change')()
    

    【讨论】:

    • 这毫无意义。他想知道adddeletechange。如果他要硬编码这些值,那有什么意义呢?
    • 这不会将您的命令组合在一起,除非您对所有命令名称进行了硬编码。这可行,但不会产生结构良好的程序。
    • @RafaelC 正如 OP 所暗示的那样,他正试图通过字符串 (['add', 'delete', 'change']) 从 commands.py 访问对象。 getattr 将使他能够通过传递字符串值来做到这一点。出于演示目的,这些名称是硬编码的。
    • @Ajax1234 嗯,我明白了。老实说我用不同的方式解释它,让我们等待OP澄清
    • @RafaelC 实际上,相对于 OP 的问题上下文,您的回答更有意义。也许OP可以澄清。
    猜你喜欢
    • 2011-01-10
    • 1970-01-01
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    • 2015-06-18
    • 1970-01-01
    相关资源
    最近更新 更多