dictionary:用Python制作菜单

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Calling a function from a string with the function's name in Python

我想我可以写一些糟糕的代码来实现这一点,但是我更愿意看到"干净的版本"。

在我看来,最好的方法是创建一个dict,它包含给定对象可以使用的各种函数。然后,当用户被指示告诉对象它在做什么时,它会根据该dict吐出一个菜单。

我到处找了一下,并没有真正发现适合我的东西,所以我想我应该试一试。但它没有成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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认为正确的东西时,程序就会给出类型错误——在本例中,输入"foo"或"bar"。事情是这样的,我知道我可以在这里写一个又长又丑的if-else来让这个例子工作,但是我想能够从self中添加/删除。更改对象的功能。因此,第三个函数Junk();Stick不能从当前的dict-menu中访问"Junk()",但可以使用一个小小的self.cmds。追加动作,我希望它能够。

该死的蟒蛇,它们是怎么工作的?这是正确的方法,还是有更简单的方法?

编辑:我的答案是在getattr的魔力中找到的。谢谢cdhowie。诀窍是将while循环更改为:result = getattr(self, cmd)()

现在我知道我的下一个任务是最终弄清楚getattr()实际上做了什么。请原谅我的新手身份,呵呵,我不知道我在写什么:)

最后编辑:虽然cdhowie的示例与原始程序兼容,但我发现ders的答案允许我在功能上做一些getattr()无法做到的事情;ders的解决方案让我更容易在其他对象中使用函数在Man的init中,我想这叫做对象合成,对吧?无论如何,getattr()都会将任何添加到self的函数归为AttributeError。除了人类,其他地方都有cmds。或者我又做了一件奇怪的事。但只要说,德斯FTW。


以你为例。cmds是一个列表,而不是字典。当self中的字符串时,它们会引发TypeError。将cmds列表作为函数调用。

创建一个字典,将函数名作为字符串与函数本身配对。

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

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

1
2
3
4
5
            if cmd in self.cmds:
                command = self.cmds[cmd]
                command()
            else:
                pass

要动态添加junk函数,可以使用update cmds:

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