2010-11-25 15 views
2

Si j'ai la classe Python suivante:Dynamiquement appelant les fonctions imbriquées basées sur des arguments

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") 

Je voudrais être en mesure de faire ce qui suit:

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

Des suggestions quant à la façon dont je peux vas-y?

Répondre

4

je ferais probablement quelque chose comme ceci:

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 

Maintenant, la classe expose une dict dont les clés sont des commandes pour l'adhésion de test. Toutes les méthodes et le dictionnaire sont créés une seule fois.

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. On dirait que tu m'as battu :). J'allais proposer quelque chose de similaire. J'ajouterais une petite chose qui est un deuxième paramètre à 'register' qui sera une liste de noms que la fonction peut être appelée en utilisant" dispatch ". Cela supprimerait l'exigence de nommage hard "handle_ *" et autoriserait plusieurs noms pour la même méthode que celle requise par l'interrogateur d'origine. –

+1

'dict' devrait être 'dict_' dans la première ligne de la méthode 'dec', non? – mshsayem

+0

@Noufal Ibrahim. Excellentes idées. Mis en œuvre. @mshsayem. Merci de m'avoir pointé. – 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)() 

Ou, en gardant votre structure actuelle:

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")]() 

Je trouve cette conception un peu bizarre, cependant. Pourquoi le dict de classe devrait-il être au courant des fonctions locales de dispatch?

+0

Je sais que je pouvais le faire, mais je voulais les garder comme un « niveau de la classe » attribue donc je peux y accéder à d'autres endroits dans le code, et pour éviter instancier le dictionnaire chaque fois que j'appelle cette méthode. –

+0

@Mike, les fonctions ne sont pas des méthodes de niveau classe comme vous les avez définies. Ce sont des fonctions internes de la méthode 'dispatch'. Le dictionnaire ne sera défini qu'au moment de la création de la classe, mais les fonctions internes seront recréées chaque fois que la fonction 'dispatch' sera exécutée. – aaronasterling

+0

Je veux les exposer en tant que niveaux de classe afin que les autres classes puissent vérifier si une commande donnée est disponible en faisant 'Test.funcs.keys()'. De cette façon, je n'ai pas besoin d'instancier un grand hachage à chaque appel de la méthode 'dispatch'. Finalement, les méthodes utiliseront des variables d'instance, donc je ne peux pas simplement en faire des méthodes d'instance, car le hash de niveau classe ne les utilisera pas. Si vous pouvez recommander une meilleure approche, je serais heureux d'avoir les yeux ouverts. –