Search code examples
pythonfunctionespeaklocal-functionsglobal-functions

Can you somehow use a local function on a global level in python?


I'm writing a program and I'm using espeak library to make it speak. The thing is I've already written my program but now I want to make a way to ask the user in the beginning if he wants the program to talk to him or just rely on reading. So if he said yes the program speaks using espeak, but if he said no the program does not use espeak, so I'm stuck at the no part.

I want to ask a question and use the right function based on the answer, but the problem is that its a local function so when you use the function espeak() it says

NameError: name 'espeak' is not defined 

Here's how what I want it:

import os
ai_talk = False
# MAKE THE AI SPEAK, OR NOT
def speak_or_not():
    global ai_talk
    
    speak_or_n = input("Do you want me to speak or not?\nPS: input Y/N\n").casefold()
    while not ai_talk:
        if (speak_or_n == "y") or (speak_or_n == "yes"):
            def espeak(text):
                command = 'espeak -v +f2 -p25 '+'"'+text+'"'
                os.system(command)
                return
            ai_talk = True

        elif (speak_or_n == "n") or (speak_or_n == "no"):
            def espeak(text):
                return
            ai_talk = True

        else:
            speak_or_n = input("You can only input Y/N").casefold()
    return

speak_or_not()

print("Hello there! how are you doing?")
espeak("Hello there! how are you doing?")

So is there a way to make that def espeak() function work on a global level so I can use it for the rest of my program?

(other than coping the hole thing and pasting it in the no section without the espeak function)


Solution

  • Here's how to write it such that speak_or_not returns the espeak function. I've added type annotations to make it easier to see how the inner function interacts with the outer function -- the speak_or_not function returns a Callable[[str], None] (a function that takes a str argument and returns None), and both version of espeak match this type.

    import os
    from typing import Callable
    
    def speak_or_not() -> Callable[[str], None]:
        """Return a function that either makes the AI speak or is a no-op."""
        speak_or_n = input("Do you want me to speak or not?\nPS: input Y/N\n").casefold()
        while True:
            if speak_or_n in ("y", "yes"):
                def espeak(text: str) -> None:
                    os.system(f'espeak -v +f2 -p25 "{text}"')
                return espeak
            if speak_or_n in ("n", "no"):
                def espeak(text: str) -> None:
                    return
                return espeak
            speak_or_n = input("You can only input Y/N").casefold()
    
    espeak = speak_or_not()
    
    print("Hello there! how are you doing?")
    espeak("Hello there! how are you doing?")
    

    Note that the global ai_talk is not necessary because this value was never used outside of the function, and in fact within the function it's only ever used to break the loop, which is unnecessary if you return once the terminating condition is met, so even within the function the value is not needed at all.