【问题标题】:there must be a better way to do this必须有更好的方法来做到这一点
【发布时间】:2013-02-23 22:33:34
【问题描述】:

这是一个丑陋的、高维护的工厂。我真的只需要一种方法来使用字符串来实例化一个名称与字符串匹配的对象。我认为元类是答案,但我不知道如何应用它:

from commands.shVersionCmd import shVersionCmd
from commands.shVRFCmd import shVRFCmd
def CommandFactory(commandnode):
    if commandnode.attrib['name'] == 'shVersionCmd': return shVersionCmd(commandnode)        
    if commandnode.attrib['name'] == 'shVRFCmd': return shVRFCmd(commandnode)

【问题讨论】:

  • 也许您想将标题更改为“按名称调用函数”?也许你可以尝试谷歌这个呢? ;)
  • 我的研究可能失败了,但我可以自信地说这不是因为缺乏尝试。不过感谢关键字提示:)

标签: python


【解决方案1】:

您可以使用globals() 函数查找全局名称,该函数返回一个字典:

from commands.shVersionCmd import shVersionCmd
from commands.shVRFCmd import shVRFCmd

# An explicit list of allowed commands to prevent malicious activity.
commands = ['shVersionCmd', 'shVRFCmd']

def CommandFactory(commandnode):
    cmd = commandnode.attrib['name']
    if cmd in commands:
        fn = globals()[cmd]
        fn(commandnode)

【讨论】:

  • 这个解决方案的一个问题:如果cmdnotcommands 中,它只会返回而不引发异常。很容易修复,但也很容易错过。并且您需要在fn(commandnode) 之前添加return
  • 是的,但最初显示的代码也是如此。我不想为这种情况发明一种行为。
  • 只是确认: shVersionCmd 和 shVRFCdm 是类(不是函数)并不重要,对吧?谢谢你。我以前没见过使用全局变量。
【解决方案2】:

这个答案How to make an anonymous function in Python without Christening it? 讨论了如何基于键干净地调用代码块

【讨论】:

    【解决方案3】:

    eval是你的朋友:

    from commands import *
    def CommandFactory(commandnode):
        name=commandnode.attrib['name']
        assert name in ( "shVersionCmd", "shVRFCmd" ), "illegal command"
        return eval(name+"."+name)(commandnode)
    

    请注意,如果您确定 name 永远不会包含任何非法命令,您可以删除assert 并将该功能变为免维护-喜。如有疑问,请将其保留并保留在一个位置。

    【讨论】:

    • eval 并不是一个好主意,它会在你的程序中引入漏洞。
    • 那么,请向我们展示您的解决方案。 eval 是一个功能,是的,它可能很危险。这并不意味着它没有用。而且你听起来好像它总是引入了漏洞,但事实并非如此,这取决于commandnode.attrib['name']的来源。
    • 不,不是没用,而是很容易过度使用的大钝锤。 globals() 在这里就足够了。
    【解决方案4】:

    我个人的偏好是转换工厂和命令实现之间的依赖关系,以便每个命令都向工厂注册自己。

    示例实现:

    文件命令/__init__.py:

    import pkgutil
    import commands
    
    _commands = {}
    
    def command(commandCls):
        _commands[commandCls.__name__] = commandCls
        return commandCls
    
    def CommandFactory(commandnode):
        name = commandnode.attrib['name']
        if name in _commands.keys():
            return _commands[name](commandnode)
    
    # Load all commands
    for loader, module_name, is_pkg in  pkgutil.walk_packages(commands.__path__):
        if module_name!=__name__:
            module = loader.find_module(module_name).load_module(module_name)
    

    文件命令/mycommand.py:

    from commands import command
    
    @command
    class MyCommand(object):    
        def __init__(self, commandnode):
            pass
    

    小测试:

    from commands import CommandFactory
    
    # Stub node implementation
    class Node(object):
        def __init__(self, name):
            self.attrib = { "name": name }
    
    if __name__=='__main__':
        cmd = CommandFactory(Node("MyCommand"))
        assert cmd.__class__.__name__=="MyCommand", "New command is instance of MyCommand"
        cmd = CommandFactory(Node("UnknownCommand"))
        assert cmd is None, "Returns None for unknown command type"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多