Trying to exit from a program is not working - python-3.x

I'm currently teaching myself python from books, YouTube, etc. Recently, I've started from Python Tutorial - Python for Beginners [Full Course]. I've been trying to make some bigger programs by myself (learning from python documentation) than the ones in this video. Hence, this is my question:
I'm trying to find a solution for how to exit from a function the_func after pressing keys (n, q ,e). Unfortunately, after choosing an option to not try again, the loop is still running. This is the output copied from the terminal:
Guess the secret number: 1
Guess the secret number: 2
Guess the secret number: 3
Sorry, you are loser!
-------------------------
Would you tray again? Y/N
y
Guess the secret number: 9
The secret number was 9.
You are won!
-------------------------
Would you tray again? Y/N
n
Thank you!
Guess the secret number:
You can see that even after choosing n, it's starting again.
This is the code:
# Guessing Game
def the_func() :
again = 'Would you tray again? Y/N'
scecret_number = 9
guess_count = 0
guess_limit = 3
while guess_count < guess_limit:
guess = int(input("Guess the secret number: "))
guess_count += 1
if guess == scecret_number:
print(f"The secret number was {scecret_number}.\nYou are won!")
break
else:
print("Sorry, you are loser!")
print("-" * int(len(again)), "\n")
print(again)
answer = input(" ")
while True:
if answer.lower() == "y":
the_func()
#elif answer.lower() == "n":
if answer.lower() in {'n', 'q', 'e'}:
print("Thank you!")
#return
break
# else:
# print("Sorry, that was an invalid command!")
the_func()
the_func()
Any help is appreciated.

The problem you are experiencing is known as Recursion. In simple words, the function is getting called repeatedly at the end of it. Here's the example that might clear it more to you:
def foo()
while True:
print('xyz')
break
foo() # Calling the function at the end causes recursion, i.e., infinite looping of the function (unless 'it is returned before the call')
foo()
You might ask, what is the solution?
As said above, you can prevent infinite recursion of a function (if you want to), by implicit returning the function using return.
Your answer is present in your code only. Let me show you where:
while True:
if answer.lower() == "y":
the_func()
#elif answer.lower() == "n":
if answer.lower() in {'n', 'q', 'e'}: # I would use 'elif' here, if the above 'if' statement did not have the calling of the same function. But, here, using 'if' or 'elif' doesn't matter really.
print("Thank you!")
return
#break
#else:
#print("Sorry, that was an invalid command!")
I hope you understood what you are doing wrong, and if you have any doubts, just add a comment and I would help as fast as I can.

Haaa.. DONE, So what we need
while True:
if answer.lower() == "y":
return the_func() ***# <= add return to the_func()***
if answer.lower() in {'n', 'q', 'quit', 'e', 'exit'}:
print("Thank you!")
return
#the_func() #<== mark as comment, and func not will bee looped again the_func()
the_func()
However, #Alex thank you!

Related

How to separate if statements and exit code to execute the program properly?

I was doing beginner python projects as I am a beginner still. I came upon this guessing game project and Wanted to do a Yes or No type of question on whether the user wants to take part in guessing or not.
If user enters Yes, they take part, if anything else ("No" or anything the program exits the code)
However I seem to be doing something wrong, when I enter Yes, the program exits the code anyway. What am I doing wrong? Thank you all,
Here is the code. I am only posting a part of it, where I most probably get the error.
import random
guess = 0
name = input("Hello what is your name?: ")
num = random.randint(1 , 50)
response = input("Well hello there " + name + " I have a number between 1-50, Want to play? You have 10 tries")
if response != "Yes" and response != "yes":
exit()
else:
while guess <= 10:
guess += 1
take = int(input("Guess a number!"))
if take == num:
print("You win! You guessed " + str(guess) + " times")
elif take > num:
print("Too high!")
elif take < num:
print("Thats too low!")
elif take >= guess:
print("You lose... The number was "+ num)
if response != "Yes" or "yes":
equates to this:
if response != "Yes" # which resolves to False when response is a 'no'
OR
"yes" # which is a non-empty string, which Python equates to True.
So basically, you code is equivalent to:
if False OR True:
and thus it always runs the exit() function.
In addition to the items noted above, the if statement should be checking both conditions and thus it should be using and instead of using or, as shown below (HT to #tomerikoo):
What you need is TWO separate tests in the if statement:
if response != "Yes" and response != "yes":
If you believe that you might have other versions of yes answers OR if you think that doing a comparison against a general sequence of terms might be easier to understand, you can also do this test instead:
if response in ['Yes', 'yes', 'y', 'Y', 'YES']:
Debugging:
For folks who are new to programming, in Python, it is sometimes fast and easy to use a simple print() function to rapidly evaluate the current state of a variable, to ensure that it really points at the value you believe it does. I added a print statement below with a comment. It would be beneficial to see what value is associated with response. (Caveat: there are professional tools built into Python and into code editors to help with watching variables and debugging, but this is fast and easy).
import random
guess = 0
name = input("Hello what is your name?: ")
num = random.randint(1 , 50)
response = input("Well hello there " + name + " I have a number between 1-50, Want to play? You have 10 tries")
print(response) # checking to see what was actually returned by the
# input() method
if response != "Yes" and response != "yes":
print(response) # check to see what the value of response is at
# the time of the if statement (i.e. confirm that
# it has not changed unexpectedly).
exit()
else:
while guess <= 10:
guess += 1
take = int(input("Guess a number!"))
if take == num:
print("You win! You guessed " + str(guess) + " times")
elif take > num:
print("Too high!")
elif take < num:
print("Thats too low!")
elif take >= guess:
print("You lose... The number was "+ num)
In python "non-empty random str" is True and empty string "" is False for conditional usages. So,
if "yes":
print("foo")
prints 'foo'
if "":
print("foo")
else:
print("bar")
prints 'bar'
In your case you want program to exit if response is not "Yes" or not "yes". You have two conditions evaluated in if statement:
response == "Yes" --> False
"yes" --> True
if 1 or 2 --> True --> so exit.
so it should be like that:
if response not in ["Yes", "yes"]:
exit()
else:
do_something()

Python program won't append more than one value

Whenever I try to append(guesses) to the all_guesses variable it seemingly replaces the existing value from the previous loop. I want the program to record down all the player's number of guesses per game round but it only record the most recent value. I made sure the variable isn't in the while loop so that it doesn't overwrite it, so what's wrong? I'm really new to python programming so I can't seem to figure this out. Each time I run the loop the guessed and all_guesses values are reset to their original.
This is a snippet of my program:
def main():
guesses = 0
guessed = []
all_guesses = []
guess = input('\nPlease guess a letter: ').lower()
letter = 'abcdefghi'
answer = random.choice(letter)
while len(guess) != 1 or guess not in letter:
print("\nInvalid entry! One alphabet only.")
guess = input('Please guess a letter: ')
while len(guess) < 2 and guess in letter:
if guess in guessed:
guess = input("\nYou've already guessed that! Try again: ").lower()
else:
if guess == answer:
guesses = guesses + 1
played = played + 1
print("\nCongratulations, that is correct!")
replay = input('Would you like to play again? Type y/n: ').lower()
all_guesses.append(guesses)
The short answer would be that all_guesses needs to be a global defined outside of main and the replay logic also needs to wrapped around main.
You seem to be missing logic, as you never modify guessed but expect to find things in there. And there are dead ends and other missing parts to the code. As best as I can guess, this is roughly what you're trying to do:
from random import choice
from string import ascii_lowercase as LETTERS
all_guesses = []
def main():
guessed = []
answer = choice(LETTERS)
guess = input('\nPlease guess a letter: ').lower()
while len(guess) != 1 or guess not in LETTERS:
print("\nInvalid entry! One alphabet only.")
guess = input('Please guess a letter: ').lower()
while len(guess) == 1 and guess in LETTERS:
if guess in guessed:
guess = input("\nYou've already guessed that! Try again: ").lower()
continue
guessed.append(guess)
if guess == answer:
print("\nCongratulations, that is correct!")
break
guess = input("\nIt's not that letter. Try again: ").lower()
all_guesses.append(len(guessed))
while True:
main()
replay = input('Would you like to play again? Type y/n: ').lower()
if replay == 'n':
break
print(all_guesses)

Set IF statment to check for specific characters, breaks while loop when using any character instead

I am new to python. I was making a guess the random number game, and I run into issues when having both int and str as inputs, I want the user to exit the program when pressing Q or q, while the game to check for numbers as well. After hours of googling and rewriting this is what I came up with:
#! Python3
import random
upper = 10
number = random.randint(1, upper)
print(number) #TODO: THIS LINE FOR TESTING ONLY
print("Guess a number between 1 and {}. Press Q to quit!".format(upper))
total_guesses = 0
guess = 0
while guess != number:
total_guesses += 1
guess = input()
if guess.isalpha():
if guess == "q" or "Q":
break
elif guess != "q" or "Q":
print("Please type in a valid guess.")
guess = 0
if guess.isnumeric():
guess = int(guess)
if guess == number:
print("Good job! You guessed the number in {} tries!.".format(total_guesses))
break
if guess < number:
print("Guess Higher!")
if guess > number:
print("Guess lower!")
else:
print("Valid inputs only")
guess = 0
This code ALMOST works as intended; issues I have now is that at line 13 and 14 the loop breaks every time when any letter is typed, even though I set the if statement to only check for Q or q, and I can't understand why this is doing it. Any help is appreciated!
if guess == 'q' or "Q":
The way this line is read by python is -
if guess == "q":
and also -
if "Q":
"Q" is a character, which means it's truthy. if "Q" returns True. Try:
if guess == "q" or guess == "Q":
if you feels that's too much, other options include -
if guess in ["Q", "q"]:
if guess.upper() == "Q":

Python Noob - Looking for anti-spaghetti suggestions and avoiding redundant code

First post here. I wanted to get this out to you folks because I'm looking for some insightful feedback and my approaches coming back into this world. My main experience is with Javascript on front-end work, and I hadn't written a single letter of python until yesterday afternoon, so excuse the mess.
I'm trying to see what you might see as some right-off-the-bat suggestions for abbreviating, or cleaning up this code. Maybe some glaring "please never do this again" items, or whatever insight you might have.
It's a console number guessing game, currently 1 - 10. It does run, with some bugs. The one main one that keeps tripping me up (and I'm sure it's simple) is if you guess the correct answer on the last try (3rd in this case) it does not run the 'Congrats' code. It runs the 'You failed' section.
Just looking for some suggestions or input of any kind to help assess MY thinking and approach to problem solving in this language. Also, if there's a better place on the web for this kind of nooby-ness, let me know. Thanks!
from random import randint
def guessing_game():
# Set the count and limits for the loop and game length.
guess_count = 0
guess_limit = 2
# Ask the user to take a guess.
guess = int(input("I'm thinking of a number between 1 and 10... "))
# Randomize the answer. Change the range according to preference.
the_answer = randint(1, 10)
# Start the loop
while guess_count < guess_limit:
if guess == the_answer: # The user wins and is prompted to play again. Init function would probably be useful.
print(f"Congrats, you won! The answer is {the_answer}!")
restart = input("Play again? ")
if restart.upper() == "Y":
guessing_game()
else: # The user declines to play and we break out of the loop.
print("Okay, thanks for playing! ")
break
break
elif guess != the_answer: # The user guessed the wrong answer. Ask again and add to the count.
print("Oops, try again.")
guess = int(input("I'm thinking of a number between 1 and 10... "))
guess_count += 1
if guess_count == guess_limit: # Shucks, the user failed. Inform them of their failure and offer redemption.
print('Sorry, you failed.')
restart = input("Play again? ")
if restart.upper() == "Y":
guess_count = 0
guessing_game()
else: # The user opted out of continuing this exciting adventure.
print("Okay, thanks for playing! ")
break
guessing_game()
1/23/2020 - 12:45pm EST Notice the updated code below per #An0n1m1ty. With this updated code I can make up to 4 incorrect guesses before it terminates. On the second wrong guess, there is no 'Oops' message printed, and on the fourth wrong guess, the program just ends. No 'You lost' message or prompt to play again.
Winning, however, seems to be functioning correctly. I will go over it and try to identify the logic behind the current behavior. You can see the changes I made with comments starting with 'CHANGE'.
Also, if there's a better place to display these code updates, please let me know. I didn't put it in an answer, because it's not an answer. Sorry, I'm a noob.
from random import randint
def guessing_game():
# Set the count and limits for the loop and game length.
guess_count = 0
guess_limit = 2
# CHANGE Moved guess input from here to beginning of loop
# Randomize the answer. Change the range according to preference.
the_answer = randint(1, 10)
# Start the loop
while guess_count < guess_limit:
# CHANGE Ask the user to take a guess.
guess = int(input("I'm thinking of a number between 1 and 10... "))
if guess == the_answer: # The user wins and is prompted to play again. Init function would probably be useful.
print(f"Congrats, you won! The answer is {the_answer}!")
restart = input("Play again? ")
if restart.upper() == "Y":
guessing_game()
else: # The user declines to play and we break out of the loop.
print("Okay, thanks for playing! ")
break
break
elif guess != the_answer and guess_count != guess_limit: # CHANGE The user guessed the wrong answer.
print("Oops, try again.")
guess = int(input("I'm thinking of a number between 1 and 10... "))
guess_count += 1
elif guess_count == guess_limit: # CHANGE Shucks, the user failed. Inform them of their failure and offer redemption.
print('Sorry, you failed.')
restart = input("Play again? ")
if restart.upper() == "Y":
guess_count = 0
guessing_game()
else: # The user opted out of continuing this exciting adventure.
print("Okay, thanks for playing! ")
break
guessing_game()
1/23/2020 - 7:45pm EST So with these changes we are a lot closer. The main issue is that if you get three wrong answers, the program just terminates. No failure message or prompt to play again. I believe this is because the loop condition is met after three tries, so the loop breaks and the
elif guess_count == guess_limit:
section never has a chance to run. I solved this issue by changing
while guess_count < guess_limit:
to
while guess_count <= guess_limit:
which allows the aforementioned section to run and prompt to play again. The only thing with this solution is that the loop runs one more time beyond the guess_limit because of the <= at the beginning of the loop. So you have to reduce the guess_limit by 1 of what you actually want.
The problem I'm having now is that if I go through a round and lose (only) and choose to play again, after losing or winning that next round and choosing NOT to continue, it will say "Okay, thanks for playing" and the game immediately starts over instead of terminating. So that's where I'm at now.
Thanks so much for all your help! Sounds weird, I'm sure, but it has me looking at it fresh again.
The following code is where the issue is:
elif guess != the_answer: # The user guessed the wrong answer. Ask again and add to the count.
print("Oops, try again.")
guess = int(input("I'm thinking of a number between 1 and 10... "))
guess_count += 1
When the user finally gets the answer right, the next bit of code will execute:
if guess_count == guess_limit: # Shucks, the user failed. Inform them of their failure and offer redemption.
print('Sorry, you failed.')
restart = input("Play again? ")
if restart.upper() == "Y":
guess_count = 0
guessing_game()
else: # The user opted out of continuing this exciting adventure.
print("Okay, thanks for playing! ")
break
Now, if the user is on the 3rd guess, guess_count == guess_limit will be true and the code will execute saying "Sorry, you failed"
To fix the issue, I did the following:
Move
guess = int(input("I'm thinking of a number between 1 and 10... "))
to the first line of the loop. i.e.:
while guess_count < guess_limit:
guess = int(input("I'm thinking of a number between 1 and 10... "))
Then, change:
elif guess != the_answer:
to
elif guess != the_answer and guess_count != guess_limit:
Then, change:
elif guess != the_answer and guess_count != guess_limit: # CHANGE The user guessed the wrong answer.
print("Oops, try again.")
guess = int(input("I'm thinking of a number between 1 and 10... "))
guess_count += 1
to
elif guess != the_answer and guess_count != guess_limit: # CHANGE The user guessed the wrong answer.
print("Oops, try again.")
guess_count += 1
Then, change:
guess_limit = 2
to
guess_limit = 3
Finally, change:
if guess_count == guess_limit:
to
elif guess_count == guess_limit:
Manual while loops are almost always the wrong answer in Python. It's much simpler to do a for loop over a range that runs up to the number of times specified. In addition, Python for loops can take an else block that executes when the loop runs to completion, but doesn't execute if you break (or return, or an exception bubble out), making them ideal for "needle in a haystack" scenarios where the needle wasn't found (while loops can have else blocks as well, but for loops are the better option here). It also lets you move common code (code that executes whether they guessed correctly or not, in this case, the "new game" check) outside both for and else, so you either output one set of data within the loop and break to the common code, or the loop runs to completion, and the else block outputs a different message before the common code.
So you could could be dramatically simplified by using a for/else to:
from random import randint
def guessing_game():
# Set the count and limits for the loop and game length.
guess_limit = 2 # Allows up to two guesses
# Randomize the answer. Change the range according to preference.
the_answer = randint(1, 10)
for guess_count in range(guess_limit):
guess = int(input("I'm thinking of a number between 1 and 10... "))
if guess == the_answer:
print(f"Congrats, you won! The answer is {the_answer}!")
break # Go to common code for retry
elif guess_count != guess_limit: # Don't need to retest guess != the_answer; this is an else that never executes unless they weren't equal
print("Oops, try again.")
# Don't need to prompt for input again, we'll prompt at top of loop
else:
# Only prints if loop ran to completion without breaking
print('Sorry, you failed.')
# Common code run for both success and failure:
restart = input("Play again? ")
if restart.upper() == "Y":
guessing_game()
else:
print("Okay, thanks for playing! ")
if __name__ == '__main__': # Get in the habit of using the import guard now; it's not necessary yet, but when it is, you want to be in the habit
guessing_game()
Note that the unbounded recursion here is potentially problematic. If the user plays close to a thousand times, you'll hit the Python recursion limit. If that's a concern, I'd refactor to separate "one game" from "all games", and have "all games" be implemented as a loop, so you don't need to recurse to play another game:
from random import randint
def play_one_game():
# Set the count and limits for the loop and game length.
guess_limit = 2 # Allows up to two guesses
# Randomize the answer. Change the range according to preference.
the_answer = randint(1, 10)
for guess_count in range(guess_limit):
guess = int(input("I'm thinking of a number between 1 and 10... "))
if guess == the_answer:
print(f"Congrats, you won! The answer is {the_answer}!")
break # Go to common code for retry
elif guess_count != guess_limit: # Don't need to retest guess != the_answer; this is an else that never executes unless they weren't equal
print("Oops, try again.")
# Don't need to prompt for input again, we'll prompt at top of loop
else:
# Only prints if loop ran to completion without breaking
print('Sorry, you failed.')
def guessing_game():
keep_playing = True
while keep_playing:
play_one_game()
# Common code run for both success and failure:
keep_playing = input("Play again? ").upper() == "Y"
print("Okay, thanks for playing! ")
if __name__ == '__main__': # Get in the habit of using the import guard now; it's not necessary yet, but when it is, you want to be in the habit
guessing_game()
This approach also helps minimize complexity; guessing_game used to do two barely related things:
Play a single instance of the game
Determine whether or not to play another game
Separating them out keeps the two behaviors separated, so understanding either feature is easier in isolation.
One last suggestion: There's no great way to handle the "don't give the Oops message on the last loop" case I can think of off-hand, but there are "not awful" ways to remove the need for a test that will fail on most loops. The simplest approach is to echo the extra message as part of the input, using an extra message that's initially empty, and unconditionally set to the "oops" message afterwards, so it's used for all subsequent loops. To do so, you'd just replace:
for guess_count in range(guess_limit):
guess = int(input("I'm thinking of a number between 1 and 10... "))
with:
extra = '' # No oops message the first time
for _ in range(guess_limit): # Don't even name guess_count, we never use it; by convention, _ means unused variable
guess = int(input(extra + "I'm thinking of a number between 1 and 10... "))
extra = "Oops, try again.\n" # From now on, oops incorporated into prompt
allowing you to completely remove:
elif guess_count != guess_limit:
print("Oops, try again.")
replacing a per loop conditional branch with unconditional execution paths (which also makes code easier to test, since there are fewer unused or lightly used paths).

cannot fix this "else" statement error

the second "else" statement gives a syntax error. I don't understand why. what is wrong with the code?
Pardon me, still a beginner
while True:
guess = input("Guess a letter or the whole word: ")
if guess == word:
print("Yaye, you've won and have saved my neck!")
break
else:
for letter in letters:
if letter in letters:
continue
else:
guesses -= 1
word_guess(guesses)
if guesses == 0:
break
You can see in the Python 3 flow control documentation an example of an if statement. It can only have one else statement because that is what is run when all other cases (if and elif) didn't match. When are you expecting the second else to run?
As was pointed out in another answer, indentation in python matters.
Is this perhaps the indentation you are looking for?
while True:
guess = input("Guess a letter or the whole word: ")
if guess == word:
print("Yaye, you've won and have saved my neck!")
break
else:
for letter in letters:
if letter in letters:
continue
else:
guesses -= 1
word_guess(guesses)
if guesses == 0:
break

Resources