Revisiting Functions In Python 3 - python

In my script I have four functions that work like this:
def function_four():
# Does Stuff
function_one()
def function_three():
# Does Stuff
function_two()
def function_one():
usr_input = input("Options: '1') function_three | '2') Quit\nOption: ")
if usr_input == '1':
function_three()
elif usr_input == '2':
sys.exit()
else:
print("Did not recognise command. Try again.")
function_one()
def function_two():
usr_input = input("Options: '1') function_four | '2') function_three | '3') Quit\nOption: ")
if usr_input == '1':
function_four()
elif usr_input == '2':
function_three()
elif usr_input == '3':
sys.exit()
else:
print("Did not recognise command. Try again.")
function_one()
I need to know if this will cause the problem I think it will: the functions never closing, causing lots of open functions (and, presumably, wasted memory and eventual slow-down) to appear, never to disappear until the user quits the script. If true, then this would most likely be bad practise and inadvisable, meaning that there must be an alternative?

Whenever you have Python code that is:
Recalling The Same Function So That If The User Does Not Chose One Of The Other Statements Then They Can Try Again Rather Than The Program To Stop Working,
you are almost always better off replacing the recursive call with a loop. In this case the recursion is completely unnecessary, probably wastes resources and arguably makes the code harder to follow.
edit: Now that you've posted the code, I'd suggest recasting it as a state machine. The following page provides a summary of Python modules that could be useful: link.
Even without any additional modules, your code lends itself to a simple non-recursive rewrite:
import sys
def function_four():
# Does Stuff
return function_one
def function_three():
# Does Stuff
return function_two
def function_one():
usr_input = input("Options: '1') function_three | '2') Quit\nOption: ")
if usr_input == '1':
return function_three
elif usr_input == '2':
return None
else:
print("Did not recognise command. Try again.")
return function_one
def function_two():
usr_input = input("Options: '1') function_four | '2') function_three | '3') Quit\nOption: ")
if usr_input == '1':
return function_four
elif usr_input == '2':
return function_three
elif usr_input == '3':
return None
else:
print("Did not recognise command. Try again.")
return function_two
state = function_one
while state is not None:
state = state()
Note that the functions no longer call each other. Instead, each of them returns the next function to call, and the top-level loop takes care of the calling.

Related

Python: How to determine if a String can be converted to an integer with no issues

I am writing a program that has multiple functions to execute, and the user selects which one runs by inputting a number. I also want the user to be able to let the user cancel the request by typing "cancel".
Right now this is my code:
func = input("Requested Operation: ")
if func == 'Cancel' or func == 'cancel':
break
elif func == '' or func == ' ' or func == '0':
func = 0
elif type(int(func)) is int:
func = int(func)
else:
fail = True
Context: Function 0 displays a list of the available items to choose from, so I want whitespace or 0 to work as displaying the project list. If the user types "Cancel" or "cancel" it will end the program.
The problem I am having is line 6 (the 2nd elif). My goal is to set the fail variable to True if the user inputs a string that isn't a cancel command, so the code breaks right there and starts over. The problem is, how do I preemptively check if a string can be converted to an integer in the first place? My current iteration returns the error invalid literal for int() with base 10: 'asdg' (asdg being the random nonsense that should make fail = True).
Also, I understand this method is probably super inefficient. Essentially, I want the conditional to be "if func is cancel, break. If func is whitespace or '0', then it equals 0. If func is some non-0 integer, convert the string to an integer and continue. Otherwise, set fail to True and break."
My knowledge of python is minimal so I would very much appreciate a full explanation or link to documentation so I can learn as much as possible.
Thanks in advance :)
Edit: This is the entire module
import projects.dice_app as dice_app
import projects.text_to_math as text_to_math
def main():
f = open("readme_files/index.txt")
p = open("readme_files/projects.txt")
print(f.read())
func = 0
while True:
fail = False
func = input("Requested Operation: ")
if func == 'Cancel' or func == 'cancel':
break
elif func == '' or func == ' ' or func == '0':
func = 0
elif type(int(func)) is int:
func = int(func)
else:
fail = True
break
if func == 0:
p = open("readme_files/projects.txt")
print(p.read())
elif func == 1:
dice_app.dice_func()
elif func == 2:
text_to_math.ttm_func()
else:
print("Invalid operation. Please try again.")
if __name__ == "__main__":
fail = False
main()
while fail == True:
main()
elif func.isnumeric():
func = int(func)
try :
func = int(func)
except ValueError:
print('not a number')
This should work

Get user input to stop a while loop

I need a way to stop a loop by getting user input without blocking the loop itself which works cross-platform.
Things I have tried:
processes (closes stdin so I can't use input())
threads (can't kill a thread if the while loop terminates and I no longer need the input())
FLAG = False
def break_main_loop(): # how do I get this to execute?
global FLAG
user_in = input()
if user_in == 'stop':
FLAG = True
return
else:
break_main_loop()
def stuff():
pass
def main():
# do stuff, e.g. getting other user input()
while True:
stuff()
if FLAG:
break
return # if I return here, I need to cancel break_main_loop's input()
main()
This will work for you and simple to use. Replace main function with this
def main():
# do stuff, e.g. getting other user input()
try:
while True:
stuff()
except KeyboardInterrupt:
print("Press 1 to quit")
return # if I return here, I need to cancel break_main_loop's input()
main()
I'd love to answer your question. You see, once you are in the main-loop, you don't necessarily need to use a FLAG variable, rather, I'd suggest doing something like this :
def break_main_loop(): # how do I get this to execute?
user_in = input()
if user_in == 'stop':
return True # Returning integer 1 also works just fine
else:
return False # Returning integer 0 also works just fine
def stuff():
pass
def main():
# do stuff, e.g. getting other user input()
while True:
stuff()
if break_main_loop(): # If the user inputs stop, the loop stops via the return statement automatically
return
main()
If you wish to get out of the loop without returning anything else and keep the main() function running for doing stuff:
def break_main_loop(): # how do I get this to execute?
user_in = input()
if user_in == 'stop':
return True # Returning integer 1 also works just fine
else:
return False # Returning integer 0 also works just fine
def stuff():
pass
def main():
# do stuff, e.g. getting other user input()
while True:
stuff()
if break_main_loop():
break
#continue doing stuff
main()
Now, there's a better way to break out of the loop without using the helper function break_main_loop(), and that's done like so:
def stuff():
pass
def main():
# do stuff, e.g. getting other user input()
while True:
stuff()
if str(input("Do you wish to continue:[y/n]")) == 'n':
break
#continue doing stuff
main()
This lets you get rid of the helper function completely.
I hope this answer was helpful. :D
try this:
class new_game:
start_game = True
# end_game = False
def break_main_loop(self): # how do I get this to execute?
print("game is ending!")
def stuff(self):
print("this is stuff happening....now...game is: ", self.start_game)
def game_loop(self):
user_in = int(input("enter '2' to end game or '1' to keep playing:" ))
if user_in == 1:
self.stuff()
self.game_loop()
else:
return self.break_main_loop()
Test_game = new_game()
Test_game.game_loop()

def() function screws with my indentation

I'm writing a basic script that scans files and moves them if they fit some criteria set by the user.
I have tried complying with what it wants but that just ends up with every line of code being indented one more than the other.
def check():
#code simplication
for i in range(2):
if fileScanDestination == '':
noValue(moveFrom)
else:
#exits
if fileMoveDestination == '':
noValue(moveTo)
else:
#exits
if specialFileExtension == '':
str(specialFileExtension) == 'nil'
else:
#exits
if fileNameSpecial == '':
str(fileNameSpecial) == str('nil')
else:
#exits
def p(text):
print(text)
#setting up tkinter
# getting variable data
p('Please enter the path for the files you would like to sort.')
fileScanDestination == str(input())
This should just have to be indented once, then exit. But since it wants to indent every new line, which just looks bad.
Try putting some pass instructions after the else statements, or some return's if you say it should exit.
You could even shorten the whole thing by doing:
if any([
fileScanDestination == '',
fileMoveDestination == '',
specialFileExtension == '',
str(specialFileExtension) == 'nil',
str(fileNameSpecial) == str('nil'),
fileNameSpecial == ''
]):
return

Python 3.4.2:My infinite function while loop is not working

My infinite while loop is not working as I expected it:
def main():
print("Type 1 to create a file")
print("Type 2 to read a file")
print("Type 3 to append a file")
print("Type 4 to calculate total of file")
print("Type 5 to end the program")
choice=input("Select a number:")
if choice == '1':
file_create(filename)
elif choice == '2':
read_file(filename)
elif choice == '3':
append_file(filename)
elif choice == '4':
add_numbers(filename)
filename=input("Give a name to your file:")
while main():
# ...
This executes the main once, but it does not loop.
Mr.Anyoneoutthere, Sylvain is absolutely correct. Since you don't understand it, I'll explain.
A loop needs a conditon:- True OR False.
So when you say:-
while True:
print('World')
is the same as:-
a = 100
while a == 100:
print('poop')
Since a == 100 would evaluate to 'True', and start a loop because you let the value remain constant, and start an infinite loop. But you can directly put the evaluation, i.e., 'True', so as to directly start an infinite loop.
As you have put:-
while main():
print('World')
So now think... while main()... main() what?... the compiler does not get any code to evaluate something into 'True' or 'False' and the loop never starts!
So your required correct code is:-
while True:
main()
def main():
# ...
# <- no return statement
while main():
# Something
The while loop loops as long as the condition is true. Here, as your main() function does not have a return statement, it doesn't return anything explicitly. So Python behave as if it were returning None. None is not true. So the condition is false and you don't execute the body even once.
What about something like that (assuming you need to execute main() until the user wants to quit):
def main():
# ...
print("Type 9 to quit")
choice=input("Select a number:")
if choice == '9':
return False
# Handle other cases
if choice == '1':
file_create(filename)
elif choice == '2':
# ...
return True
On the other hand, as suggested by #thiruvenkadam in a comment below, you could keep your main as it was written in your question, but really write an infinite loop:
while True:
main()
But that way, if you want to terminate your program gracefully , you will have to rely on some other mechanism like using exceptions...

Function called without being told to

Newish to python, working on a text adventure, testing out the use of functions.
def cell1():
loop = 1
while loop == 1:
print("ONE")
cave1 = input()
if cave1 == ("end?"):
print("\nthis should end program")
loop = 0
break
elif cave1 == ("TWO"):
global testvar
testvar = 1
option1()
else:
print("INVALID")
def option1():
print("TWO")
loop = 1
while loop == 1:
print("test1 definition")
print (testvar)
test1 = input()
if test1 == ("ONE"):
print("you pick up the cheese")
loop = 0
cell1()
elif test1 == ("THREE"):
option2()
else:
print("INVALID")
def option2():
print("THREE")
loop = 1
while loop == 1:
print("This is option 3")
test2 = input()
if test2 == ("ONE"):
print("testering2")
cell1()
elif test2 == ("TWO"):
global testvar
testvar = 2014
option1()
else:
print("INVALID")
run = True
while run == (True):
print ("testing 123")
cell1()
print("restart about to activate")
cont = input("Restart? ")
if (cont) != "yes":
break
This program should allow you to go between options (what would be rooms) and eventually in cell1, the program should be end-able.
if the program is run and "end?" is typed as the first input, the program goes into the continue bit at the bottom, however, if you go between the 'rooms' then back to cell1, typing "end?" will call option 2.
Ive had a look around and it is still baffling me, am i ding something wrong?
Any help is appreciated, thank you.
The reason "end?" only quits for the player when they are within the first cell is because you're only checking for that input therein. The execution contained within option1() and option2() doesn't affect the execution of cell1(). You're not returning anything from your option functions, nor are you changing a sentinel value.
So, there's two basic ways you could go about this.
First, you could return a value from your functions:
if option1() == "END":
break
Or, you could alter your while loop:
# is_running is defined globally
while is_running:
And then just set is_running to False in any of your methods whenever the user types "end?". That'd probably be the easiest way with the design you're using now.
I'm sure you can tell, though, that in general your program is going to get exponentially more complex as you add more rooms and your function calls get further nested.
I'm pretty sure that the issue you're having is because you don't always break out of the loop in one function when you call another function. For instance, if your entries were TWO, ONE then end?, you'd find yourself still in the cell1 loop. That's because when the inner call to cell1 returns, the control flow of the program goes back to where that function was called from, which is option1, since loop is now 0, the loop ends and option1 returns, to the outer call to cell1, where the loop is still running.
Unless you want the game you're designing to have a tree structure, where you can return to where you came from with different semantics than moving to some other place, I'd suggest using a different architecture. Rather than each of your functions calling the next function when appropriate, return that function instead. Then you'd write a single top level loop that calls the function. Here's an example where the function to be called by the top level loop is saved in a variable named state:
def cell1():
print("In cell1!")
while True:
choice = input("pick 'ONE' or 'TWO' (or type 'quit' to exit):")
if choice == "ONE":
return option1
elif choice == "TWO":
return option2
elif choice == "quit":
return None
else:
print("I'm sorry, I didn't understand that.")
def option1(): # these other two functions are very basic in my example
print("In option1!") # but you can make them as complex as you want
return option2
def option2():
print("in option2!")
return cell1
def control_loop(initial_state=cell1):
state = initial_state
while state is not None:
state = state() # the next state is the return value of the previous state
The problem is you are getting deeper and deeper within nested functions. For example, changing
if test1 == ("ONE"):
print("you pick up the cheese")
loop = 0
cell1()
to
if test1 == ("ONE"):
print("you pick up the cheese")
loop = 0
break
will allow you to run your program, enter room two, go back to room one, and "end?" will work properly. This won't fix your issues completely though because there is a similar problem where when you go from two to three where if you simply changed
if test2 == ("ONE"):
print("testering2")
cell1()
to
if test2 == ("ONE"):
print("testering2")
break
it would break the current function and go back into option1() (if you run your program, go to room two, then to room three, then back to one) where "end?" doesn't do anything. Hopefully this gets you on the right track.

Categories