2010-11-25 9 views
2

Wenn ich die folgende Python-Klasse:Dynamisch verschachtelte Funktionen auf Argumente basieren Aufruf

class Test(object): 
    funcs = { 
     "me" : "action", 
     "action": "action", 
     "say" : "say", 
     "shout" : "say" 
    } 

    def dispatch(self, cmd): 
     def say: 
      print "Nested Say" 

     def action: 
      print "Nested Action" 

     # The line below gets the function name as a string, 
     # How can I call the nested function based on the string? 
     Test.funcs.get(cmd, "say") 

Ich möchte in der Lage sein, Folgendes zu tun:

>>> Test().dispatch("me") 
Nested Action 
>>> Test().dispatch("say") 
Nested Say 

Irgendwelche Vorschläge, wie kann ich gehst du darüber?

Antwort

4

Ich würde wahrscheinlich etwas tun:

def register(dict_, *names): 
    def dec(f): 
     m_name = f.__name__ 
     for name in names: 
      dict_[name] = m_name 
     return f 
    return dec 

class Test(object): 

    commands = {} 

    @register(commands, 'foo', 'fu', 'fOo') 
    def _handle_foo(self): 
     print 'foo' 

    @register(commands, 'bar', 'BaR', 'bAR') 
    def _do_bar(self): 
     print 'bar' 

    def dispatch(self, cmd): 
     try: 
      return getattr(self, self.commands[cmd])() 
     except (KeyError, AttributeError): 
      # Command doesn't exist. Handle it somehow if you want to 
      # The AttributeError should actually never occur unless a method gets 
      # deleted from the class 

Nun macht die Klasse ein dict, dessen Schlüssel Befehle zum Testen Mitgliedschaft. Alle Methoden und das Wörterbuch werden nur einmal erstellt.

t = Test() 

if 'foo' in t.commands: 
    t.dispatch('foo') 

for cmd in t.commands: 
    # Obviously this will call each method with multiple commands dispatched to it once 
    # for each command 
    t.dispatch(cmd) 

Etc.

+1

+1. Sieht so aus als würdest du mich damit schlagen :). Ich wollte etwas Ähnliches vorschlagen. Ich würde ein kleines Ding hinzufügen, das ein zweiter Parameter für "register" ist, der eine Liste von Namen sein wird, die die Funktion aufgerufen werden kann, indem sie "dispatch" verwendet. Dies würde die harte 'handle_ *' - Benennungsanforderung entfernen und mehrere Namen für dieselbe Methode erlauben, wie sie vom ursprünglichen Fragesteller benötigt wird. –

+1

'dict' sollte 'dict_' in der ersten Zeile der 'dec' Methode sein, richtig? – mshsayem

+0

@ Noufal Ibrahim. Ausgezeichnete Ideen. Implementiert. @mshsayem. Danke für das Zeigen. – aaronasterling

2
class Test(object): 

    def dispatch(self): 
     def say(): 
      print "Nested Say" 

     def action(): 
      print "Nested Action" 

     funcs = { 
      "me" : action, 
      "action": action, 
      "say" : say, 
      "shout" : say 
     } 

     Test.funcs.get(cmd, say)() 

Oder Ihre aktuelle Struktur zu halten:

class Test(object): 

    funcs = { 
     "me" : "action", 
     "action": "action", 
     "say" : "say", 
     "shout" : "say" 
     } 

    def dispatch(self, cmd): 
     def say(): 
      print "Nested Say" 

     def action(): 
      print "Nested Action" 

     locals()[Test.funcs.get(cmd, "say")]() 

Ich finde das Design ein wenig seltsam, aber. Warum sollte das Diktat auf Klassenebene über die lokalen Funktionen des Versands Bescheid wissen?

+0

Ich weiß, ich könnte das tun, aber ich wollte, dass sie als „Klassenstufe“ halten Attribut, damit ich sie an anderen Stellen im Code zugreifen, und das Wörterbuch Instanziierung zu vermeiden Jedes Mal, wenn ich diese Methode nenne. –

+0

@Mike, die Funktionen sind keine Klassenmethoden, wie Sie sie definiert haben. Sie sind innere Funktionen der "Versand" -Methode. Das Wörterbuch wird nur definiert, wenn die Klasse zuerst erstellt wird, aber die inneren Funktionen werden jedes Mal neu erstellt, wenn die Funktion "dispatch" ausgeführt wird. – aaronasterling

+0

Ich möchte, dass sie als Klassenstufen verfügbar gemacht werden, damit andere Klassen prüfen können, ob ein bestimmter Befehl verfügbar ist, indem Sie 'Test.funcs.keys()' ausführen. Auf diese Weise muss ich bei jedem Aufruf der "Dispatch" -Methode keinen großen Hash instanziieren. Schließlich werden die Methoden Instanzvariablen verwenden, so dass ich sie nicht nur zu Instanzmethoden machen kann, da der Hash auf Klassenebene keinen Bezug zu ihnen hat. Wenn Sie einen besseren Ansatz empfehlen können, wäre ich froh, meine Augen geöffnet zu haben. –