0

I am building a virtual assistant for myself and it feels wrong and ineffective to ask for every command by an individual if like here as example:

WAKE = 'hello'


while True:
    print("Mic ready")
    text = voice.get_audio()
    if WAKE in text:
        if "note" in text:
            voice.speak("What do you want to note?")
            print("What do you want to note?")
            text = voice.get_audio()
            Vcm.note(text)
        else if "timer" in text:
            voice.speak("How long is that timer supposed to run?")
            print("How long is that timer supposed to run?")
            text = voice.get_audio()
            Vcm.timer(text)
        else:
            voice.speak("At your service")
            print("At your service")
            text = voice.get_audio()

Isn't there an more efficient way to check which command to execute?

Havelex
  • 13
  • 2
  • 3
    Welcome to Stack Overflow! If the code works and you're looking for advice on improving it, [codereview.se] is the appropriate place. But see https://codereview.meta.stackexchange.com/questions/5777/a-guide-to-code-review-for-stack-overflow-users first. – Barmar Aug 06 '21 at 15:50
  • 4
    Use a data-driven approach. Create a dictionary that maps words to search for in the input to functions to call. Then loop over the dictionary and call the corresponding function when it matches. – Barmar Aug 06 '21 at 15:50
  • 2
    Python's way for ```else if``` is ```elif``` –  Aug 06 '21 at 15:54
  • 2
    Python 3.10.xx now has a switch/case block: https://stackoverflow.com/a/66877137/421195 – paulsm4 Aug 06 '21 at 15:55

2 Answers2

4

You could separate your commands into functions:

def note():
    #note body

def timer():
    #timer body

def default():
    #default body

Then wrap your commands in a dictionary:

commands = {
    'note': lambda: note(),
    'timer': lambda: timer()
}

and define default separately:

default_command = lambda: default()

Then you can check what is in text:

for word in text:
   command = commands.get(word, None)
   if command not None:
       command()
       break
else:
    default_command()

Not sure this is what you wanted, but it may make your code more sparse if you have many commands.

EDIT:

According to Can a lambda function have a bool value of false?, lambda expressions act 'truthy'. So you could simplify the last bit too!

for word in text:
    command = commands.get(word, None)
    if command:
        command()
        break
else:
    default_command()
  • Thanks, I'll try it out! – Havelex Aug 06 '21 at 18:35
  • Good idea, however, since text is a string, word doesn't go through every word like "Hello" then "make", but it goes through every letter one by one: "H", "e", "l" etc. Therefor no command will ever be executed- – Havelex Aug 06 '21 at 18:54
  • Btw, the code above and the function declaration are in different files – Havelex Aug 06 '21 at 19:05
  • I fixed the word problem, but then when "command()" gets called it tells me that I miss a parameter for "note()", which i do, but when I change "command()" to "command(text)" it gives me an error of too many arguments – Havelex Aug 06 '21 at 19:14
  • Split the text on spaces using the .split() method. Find usage under the strings section of the docs: https://docs.python.org/3/library/stdtypes.html#str –  Aug 06 '21 at 19:31
  • 1
    I apologize, this one is on me, it took me 10 seconds of a youtube video to find a solution! Still thank you for the Idea – Havelex Aug 06 '21 at 19:42
  • @Havelex, no worries! If you're happy with solution mark it as the best fit! Glad I could help :) –  Aug 06 '21 at 19:45
1
while True:
    print("Mic ready")
    text = voice.get_audio()
    if WAKE in text:
        words = text.split(" ")
        for word in words:
            command = commands.get(word)
            if command:
                command(text)
                break
        else:
            vcm.default()
    ```
commands = {
'note': lambda text: note(text)

}

Havelex
  • 13
  • 2