boB Stepp wrote: > My son (Now 13 years old.) is merrily programming away in Python 3 (Of > course!) and has many projects he is working on. Today he resumed > work on his efforts to create a natural language parser, which he > hopes will enable people to type in commands to a DnD-like game using > natural English. An ambitious project to be sure! He asked me if > there was any way that if he identified a word in the user's input > corresponding to one of his functions or methods, if he could use that > word to run a function of the same name. I said I had done something > once where I used the word as a dictionary key, which was then used to > call the function. After fumbling around a bit I came up with the > following program for him to play around with: > > ========================================================================== > #!/usr/bin/env python3 > > def spam(phrase): > print('spam function said: ', phrase) > > def ham(phrase): > print('ham function said: ', phrase) > > def eggs(phrase): > print('eggs function said: ', phrase) > > def get_input(): > function = input('Which do you want: spam, ham or eggs?\n') > phrase = input('\nHow do you want your food to be prepared?\n') > return function, phrase > > def check_fcn_input(function): > valid_fcns = ['spam', 'ham', 'eggs'] > if function in valid_fcns: > return True > else: > return False > > def run_fcn(function, phrase): > fcn_dict = {'spam': spam, 'ham': ham, 'eggs': eggs} > fcn_dict[function](phrase) > > def main(): > function, phrase = get_input() > while not check_fcn_input(function): > print("You made an invalid food choice! Let's try this again!") > function, phrase = get_input() > run_fcn(function, phrase) > > if __name__ == '__main__': > main() > ========================================================================== > > This works, but I cannot but help wondering if there is a more direct > approach? Given the above three functions spam(), ham() and eggs(), > and given a string 'ham', what is the quickest, most direct way to run > that function? Or similarly for the other two?
If you are not bothered about security, getattr(module, function_name)() or even eval(). But usually the approach you have chosen is preferrable. > Oh, and I suppose I should ask for a critique of the code as written > for appropriate Python style, proper targeted function use, etc. I am > always eager to learn! > if function in valid_fcns: > return True > else: > return False should really be spelt return function in valid_fcns and personally I wouldn't mind to type the few extra chars to make it return function in valid_functions ;) > However, I did not use doc strings for the > functions or write tests. If I were actually planning on using this > for real, I would have done so. > > Hmm. It bothers me that in check_fcn_input() I have a list valid_fcns > and in run_fcn() I have a dictionary fcn_dict. These contain > essentially the same information. Would this be a case for a global > function dictionary (Which I could also use to check for valid > functions.) or perhaps a class which only exists to have this function > dictionary? A global is indeed better than the duplicate information in your list and dict. Here's another option, return the function instead of information about its existence: def find_function(function): fcn_dict = {'spam': spam, 'ham': ham, 'eggs': eggs} return fcn_dict.get(function) def main(): while True: function_name, phrase = get_input() function = find_function(function_name) if function is not None: break print("You made an invalid food choice! Let's try this again!") function(phrase) If want to try the class-based approach you can steal from the cmd module: class Lookup: prefix = "do_" def do_spam(self, phrase): print('spam function said: ', phrase) def do_ham(self, phrase): print('ham function said: ', phrase) def do_eggs(self, phrase): print('eggs function said: ', phrase) def known_names(self): offset = len(self.prefix) return sorted( name[offset:] for name in dir(self) if name.startswith(self.prefix) ) def get_input(self): names = self.known_names() names = ", ".join(names[:-1]) + " or " + names[-1] function = input('Which do you want: {}?\n'.format(names)) phrase = input('\nHow do you want your food to be prepared?\n') return function, phrase def find_function(self, function): return getattr(self, self.prefix + function, None) def one_cmd(self): while True: function_name, phrase = self.get_input() function = self.find_function(function_name) if function is not None: break print("You made an invalid food choice! Let's try this again!") function(phrase) def main(): main = Lookup() main.one_cmd() The name mangling with the do_-prefix prevents that the user can invoke arbitrary methods. _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor