在Python中制作菜单
可能重复的问题:
在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 个回答
在你的例子中,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})