在Python中制作菜单

8 投票
1 回答
3956 浏览
提问于 2025-04-17 00:49

可能重复的问题:
在Python中通过字符串调用函数

我觉得我可以写一些很糟糕的代码来实现这个功能,但我更想看到一个“干净的版本”。

我认为好的方法是创建一个字典,这个字典里保存了某个对象可以使用的各种函数。然后,当用户被要求告诉对象它在做什么时,它就会根据这个字典显示一个菜单。

我搜索了一下,没找到适合我的东西,所以我决定试试看。结果,没成功。

class Man(object):
    def __init__(self):
        self.cmds = ['foo', 'bar']

    def foo(self):
        print "Foo called."

    def bar(self):
        print "Bar called."

    def junk(self):
        print "Junk called." ##not in dict, on purpose, will explain

    def menu(self):
        while True:
            print "List of actions:"
            for acts in self.cmds:
                print acts
            cmd = raw_input("> ")
            if cmd in self.cmds:
                cmd()    ##doesn't work.
                         ##neither did self.cmd() (got AttributeError, obviously)

                result = getattr(self, cmd)() ## this works! thanks cdhowie
            else:
                pass

Stick = Man()
Stick.menu()

如果不明显的话,程序在我输入一些被if-else判断为真的内容时会报TypeError——在这个例子中,输入'foo'或'bar'都会出错。问题是,我知道我可以在这里写一个又长又丑的if-else语句来让这个例子工作,但我想要能够通过添加或删除self.cmds来改变对象的功能。因此,第三个函数Junk(); Stick无法从当前的字典菜单中访问'Junk()',但我希望通过一点self.cmds.append的操作能够做到。

真是让人抓狂的Python,这些东西是怎么工作的?这样做对吗,还是有更简单的方法?

编辑:我的答案在于getattr的魔力。谢谢cdhowie。诀窍是把while循环改成这样:result = getattr(self, cmd)()

我现在知道我的下一个任务是搞清楚getattr()到底是干什么的。请原谅我这个新手,嘿,我不知道我在写什么 :)

最后编辑:虽然cdhowie的例子在原始程序中有效,但我发现ders的答案让我可以做一些我用getattr()无法做到的功能;ders的解决方案让我更容易在Man的init中使用其他对象的函数——我想这叫“对象组合”,对吧?无论如何,getattr()会对从Man以外的地方添加到self.cmds的任何函数报AttributeError。或者我可能又在搞怪。但可以说,ders真是太棒了。

1 个回答

3

在你的例子中,Man.cmds是一个列表,而不是字典。所以当你试图把self.cmds列表中的字符串当作函数来调用时,就会出现类型错误。

你需要创建一个字典,把函数名(以字符串形式)和对应的函数本身配对在一起。

    def __init__(self):
        self.cmds = {'foo':self.foo, 'bar':self.bar}

在你的菜单函数中,检查用户输入的函数名是否有效。如果有效,就从字典中取出这个函数并调用它。

            if cmd in self.cmds:
                command = self.cmds[cmd]
                command()
            else:
                pass

如果你想动态添加一个垃圾函数,你可以使用update来更新cmds:

Stick.cmds.update({'junk':Stick.junk})

撰写回答