How to continuously prompt for user input? - python

I'm writing a function that prompts for input and then returns different results based on the input and then asks for input again. I've got it returning the correct values, but I'm not sure how to make it prompt for input again.
Here's the actual code of the function:
def interact():
command = raw_input('Command:')
command = command.split(' ')
if command[0] == 'i':
bike_name = command[1] + ' ' + command[2]
return get_product_id(products, bike_name)
if command [0] == 'n':
return get_product_name(products, command[1])
if command[0] == 'c':
return compute_cost(products, part, command[1])
if command[0] == 'p':
return get_parts(products, command[1])
In each line with return in it, it is simply calling up a previously defined function. The products and part are dictionaries, defined previously.
I can only use the builtin functions.

I would do it with a while loop. Like This:
while True:
com = raw_input('Command:').split()
if len(com) == 0:
break
elif com[0] == 'i':
bike_name = command[1] + ' ' + command[2]
return get_product_id(products, bike_name)

You've done most of the work, you just need this:
while True:
print interact()

There is no need to take so much pain and write your own command line interpreter.
Look at this: http://docs.python.org/2/library/cmd.html

One way is to put it in a while loop, and then also check for an exit input to break out.

Call the method inside an (end-less) loop:
while True:
some_method()

Related

Using Python for default parameters

I am currently learning python and stuck on a coding exercise. I am trying to achieve the result as shown on the image1. I am stuck on the overall code. I also not sure how to incorporate the "quit", so that the program terminates.
Image1
def tester(result):
while tester:
if len(result)< 10:
return print(givenstring)
else:
return print(result)
def main():
givenstring = "too short"
result=input("Write something (quit ends): ")
if __name__ == "__main__":
main()
For your problem, you need to have a variable that is your Boolean (true/false) value and have your while loop reference that. currently your while loop is referencing your function. inside your main function when you get your user input you can have a check that if the input is "quit" or "end" and set you variable that is controlling your loop to false to get out of it.
you also are not calling your tester function from your main function.
You missed into main() function to call your function, like tester(result). But such basics should not be asked here.
def tester(result):
if len(result)< 10 and result != 'quit':
givenstring = "too short"
return print(givenstring)
else:
return print(result)
def main():
result=None
while True:
if result == 'quit':
print("Program ended")
break
else:
result=input("Write something (quit ends): ")
if result.lower() == 'quit':
result = result.lower()
tester(result.lower())
if __name__ == "__main__":
main()

while loop, asking user if he wants to repeat the program problem

So I have this while loop which asks user if he/she wants to repeat the program. All works good but I was trying it out and found out that after user repeats the program and when asked second time if he/she wants to repeat, I choose no. But the program still repeats although it prints that program is closing, how can I fix this?
edit: I've fixed it with changing break with return and adding return after main()
def main():
endFlag = False
while endFlag == False:
# your code here
print_intro()
mode, message=get_input()
ui = str.upper(input("Would you like to repeat the program again? Y/N: "))
while True:
if ui == "Y":
print(" ")
main()
elif ui == 'N':
endFlag = True
print("Program is closing...")
break
else:
print("wrong input, try again")
ui = str.upper(input("Would you like to repeat the program again? Y/N: "))
This is because main() function is being called recursively & endFlag is a local variable.
So when you first put 'y' , then another main() is recursively called from the first main() function.
After you put 'n' which ends this second main() and return to the first main which still in a loop with endFlag (local variable) with value as false.
So, need to change,
Either
endFlag variable as global ( i.e. defined outside main function )
Or,
some program exit function in place of break
The thing is that you're doing recursion over here, i.e. you're calling method main() inside main() and trying to break out of it the way you've done is not gonna work (well, you're know it :) )
Second - you don't need a forever loop inside a first loop, you can do it with one simple loop and break.
Here it is:
def print_intro():
intro = "Welcome to Wolmorse\nThis program encodes and decodes Morse code."
print(intro)
def get_input():
return 'bla', 'bla-bla-bla'
def main():
while True:
# your code here
print_intro()
mode, message = get_input()
ui = str.upper(input("Would you like to repeat the program again? Y/N: "))
if ui == "Y":
print(" ")
elif ui == 'N':
print("Program is closing...")
break
else:
print("wrong input, try again\n")
main()
this is what you should do:
(BTW this is a piece of example code)
while True
name = input('Enter name:')
print ('Hi '+ name)
exit = input('Do you want to exit?(Y/N)')
if exit == 'N' or 'n':
break
(Im not sure if i put the indents correctly )
You can apply this concept into your code.

Continue for loop without iterating

Is there a keyword that I can use to iterate a for loop without stepping the iterator? I know that it's possible to do this without such a command, by using a while loop and iterating manually, but it would greatly simplify things, in this instance, if I could just use a for loop, since continuing without iteration is the exception, not the rule. (there will significantly more conditions added to this when it is complete, all of which will require iteration). Here's my code (or, what I've written so far):
for line in file_as_list:
response = input(line)
if response.lower() == 'help':
self.show_command_list()
response = input(line)
if response.lower() == 'loc':
self.show_location(file_as_list, location)
response = input(line)
if response.lower() == 'exit':
save_changes = input('Would you like to save the changes you have made? (Y/N) ')
while (save_changes.upper() != 'Y') & (save_changes.upper() != 'N'):
save_changes = input('That is not a valid response. Try again. ')
if save_changes.upper() == 'N':
temp = file_as_list
print('Changes were not saved.')
else:
for line in file_as_list[location:]:
temp.append(line)
print('Changes were saved.')
break
if response.lower() == 'inline':
line += ' //' + input(line + ' //')
print('Line generated: ' + line)
location += 1
temp.append(line)
I think you want two nested loops. Try something like this:
for line in file_as_list: # outer loop
while True: # inner loop
response = input(line).lower()
if response == 'help': # these two options don't exit the inner loop
...
elif response == 'loc': # so they won't consume a line from the for loop
...
else:
break
if response == 'exit': # these other two conditions are outside the while loop
...
elif response == 'inline': # so a new line will be fetched after they run
...
If either of the first two conditions are met, the inner loop will keep on running without changing line. Only if the break gets hit will the inner loop end, and the other conditions get tested. After they do their thing, a new value will be assigned to line, as the for loop continues iterating.
Unrelated to your main question, I also changed the input line to call lower on the input immediately before saving it to response. That means the conditions don't need to keep calling it repeatedly. Your code isn't wrong there, but if you never care about the user's capitalization, throwing it away right off the bat can simplify things.
You can use an explicit iterator like
it = iter(file_as_list)
for line in it:
input(line)
...
input(next(it))
and so on. Just be sure to properly handle the case where you run out of lines!
You have two types of commands: ones that advance the iterator, and ones that don't. You could also call it action vs descriptive commands. Your best bet conceptually is to have a while loop that will continue to seek input until you get an action command. This while loop will live inside the existing for loop.
The advantage of this is that currently, your descriptive commands like "help" and "loc" can't be repeated, but you probably want them to be.
Another decision I would recommend is to use distinct functions to implement each command. By giving the commands a consistent interface, you make the code easier to maintain and understand. By registering the commands in a dictionary, you can make your lookup faster and more flexible.
The following concept has a bunch of functions that return a tri-state boolean value and an update. The boolean is True if the command wants to stay on the current line, False to continue. None to exit. The line update is usually just the input.
# in __init__
...
self.command_map = {
'help': self.help,
'loc': , self.loc,
'exit': self.exit,
'inline': self.inline,
}
self.temp = []
...
def help(self, file_as_list, location, line):
self.show_command_list()
return True, line
def loc(self, file_as_list, location, line):
self.show_location(file_as_list, location)
return True, line
def exit(self, file_as_list, location, line):
save_changes = ''
while len(save_changes) != 1 or save_changes.upper() not in 'YN':
save_changes = input('Would you like to save the changes you have made? (Y/N) ')
if save_changes.upper() == 'N':
self.temp = file_as_list
print('Changes were not saved.')
else:
self.temp.extend(file_as_list[location:])
print('Changes were saved.')
return None, line
def inline(self, file_as_list, location, line):
line += ' //' + input(line + ' //')
print('Line generated: ' + line)
return True, line
def process(self):
for location, line in enumerate(file_as_list):
stay = True
while stay:
response = input(line)
command = command_map.get(response.casefold())
if command is None:
print(f'Command "{response}" not found. Try again')
else:
stay, line = command(file_as_list, location, line)
if stay is None:
break
self.temp.append(line)
Given command_map, you can do lots of things easier: for example, you can reimplement show_command_list to do something with sorted(command_map.keys()). I'm sure you can see how relatively easy it is to add commands to your list. You don't have to repeat boilerplate code, just be careful with the inputs and return values.
This construction is also much easier to iterate manually if you don't like the idea of having nested loops:
def process(self):
stay = False
iterator = enumerate(file_as_list)
while True:
if not stay:
try:
location, line = next(iterator)
except StopIteration:
break
response = input(line)
command = command_map.get(response.casefold())
if command is None:
print(f'Command "{response}" not found. Try again')
stay = True
else:
stay, line = command(file_as_list, location, line)
if stay is None:
break
if not stay:
self.temp.append(line)
As you can see, this method requires quite a bit more special handling for the various conditions.

UnpicklingError: pickle data was truncated when trying to read a dictionary from a shelved file

I'm a teacher, and I'm trying to write a simple function that saves my students' emails in a dictionary for use in another program. I need the dictionary to be saved across multiple executions, so I'm trying to use shelve to save it; however, after running the function for a second time, I get an unpickling error saying the pickle data was truncated. Here is the code:
shelfFile = shelve.open('mydata')
studentEmails = shelfFile['studentEmails']
def inputEmails():
while True:
nameInput = input('Name: ')
if nameInput == '':
break
emailInput = input('Email: ')
if emailInput == '':
print('Email not entered. Please try again.')
continue
while True:
print('Is this information correct? [Y]es or [N]o')
print('Name: ' + nameInput)
print('Email: ' + emailInput)
correctChoice = input('[Y] or [N]: ').upper()
if correctChoice == 'Y':
studentEmails[nameInput] = emailInput
break
elif correctChoice == 'N':
print('Okay. Please input again.')
break
else:
print('I did not understand that response.')
inputEmails()
shelfFile['studentEmails']=studentEmails
shelfFile.close()
I create the empty dictionary shelfFile['studentEmails'] in the shell before I run the program. It will run fine the first time, but give me the _pickle.UnpicklingError: pickle data was truncated error when I try to assign the shelfFile back to studentEmails. I'm new at this and still learning, so I appreciate the help.
I just had the same problem, and after a little investigation I realized it probably happened because I stopped my program like a jerk (terminated it in the middle of using the shelve).
So I deleted my shelve and created it again and everything worked fine.
I assume you had the same error, maybe you exited your infinite while loops by terminating the program or something?
After toying around with things and reading a few other websites, I was able to achieve what I wanted using pickle instead of shelve. Here is what the code looks like now:
import pickle
loadData = open('saveData.p','rb')
studentEmails = pickle.load(loadData)
loadData.close()
def inputEmails():
while True:
nameInput = input('Name: ')
if nameInput == '':
break
emailInput = input('Email: ')
if emailInput == '':
print('Email not entered. Please try again.')
continue
while True:
print('Is this information correct? [Y]es or [N]o')
print('Name: ' + nameInput)
print('Email: ' + emailInput)
correctChoice = input('[Y] or [N]: ').upper()
if correctChoice == 'Y':
studentEmails[nameInput] = emailInput
break
elif correctChoice == 'N':
print('Okay. Please input again.')
break
else:
print('I did not understand that response.')
inputEmails()
saveData = open('saveData.p','wb')
pickle.dump(studentEmails,saveData)
saveData.close()
This works just fine for what I'm doing. I had to create the studentEmails dictionary in the shell with placeholders as pickle doesn't allow for empty dictionaries.

How to use commands again without copy and pasting them?

I don't really know how to explain this exactly.. But I'm making a text adventure for learning the basics. Now I want to make a gold and money system, I am using def... things for different levels and such. but in every prompt I have to put in if the user types gold, or inv it shows the inventory and then go back to the def where it was.. which I find irritating to do every time. And I forget it too at some periods. I want to do it as default in the prompt.
I have a def prompt(): that is this easy code:
def prompt():
x = input('Type a command: ')
return x
and if I put it there it just ends the code. I have to do this in every prompt:
def AlleenThuis():
command = prompt()
if command == '1':
print()
elif command == '2':
print()
elif command == '3':
print()
elif command == '4':
print()
elif command == 'geld': #Actions start here
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
return AlleenThuis()
elif command == 'inv':
if not inv:
print("\n\tYou don't have any items..\n")
return AlleenThuis()
else: #The else has to stay in this place because it's part of the 'if not inv:' guys.
print('\n\t' + str(inv) + '\n')
return AlleenThuis()
#Actions end here
So if there's any way to just implement it so that I don't have to put it in again every time that would be awesome!
Thanks.
EDIT: It looks like you guys aren't understanding what I'm saying, so I have 2 images.
So..
http://i.imgur.com/GLArsyu.png (I can't post pictures yet =[ )
As you can see in this picture, I have included gold and inv.
But in http://i.imgur.com/V3ZhJ36.png I have also done that, so I have coded that in the code again, that is what I Don't want!
I just want to have that in the code 1 time, and let the gold and inventory show all the time when the player enters the commands for them!
On a more fundamental level, the object-oriented paradigm elegantly solves the problem of repetitive code like this:
global gold = 0
def cave():
print "You are in a cave."
print "You have %i gold." % gold
direction = input()
if direction = 'N':
stream()
elif direction == 'S':
house()
elif direction == 'W':
mountain()
elif direction == 'directions':
print "You can go North, West, or South."
else:
print "You cannot go there."
def stream():
print "A small stream flows out of the building and down a gully."
print "You have %i gold." % gold
direction = input()
if direction == 'N':
tree()
elif direction == 'S':
cave()
elif direction == 'directions':
print "You can go North or South."
else:
print "You cannot go there."
def main():
cave()
by turning it into something like this:
class Location:
map = { 'cave': {
'description': 'You are in a cave.',
'directions': { 'N': 'stream', 'S': 'house', 'W': 'mountain' } },
'stream': {
'description':
'A small stream flows out the building and down a gully.',
'directions': { 'N': 'tree', 'S': 'cave' } } #...
}
def __init__ (self):
self.location = 'cave'
def enter (self, direction):
self.location = self.map[self.location]["directions"][direction]
print self.map[self.location]["description"]
def directions(self):
return self.map[self.location]["directions"].keys()
def readable(self, dirs):
readable = { 'S': 'South', 'N': 'North', 'W': 'West', 'E': 'East' }
return [readable[d] for d in dirs]
class Inventory:
def __init__ (self):
self.inventory = { 'gold': 0 }
def query (self):
print "You have %i gold." % self.inventory['gold']
def main:
loc = Location()
inv = Inventory()
while True:
directions = loc.directions()
action = raw_input()
if action in directions:
loc.enter(action)
inv.query()
elif action == 'directions':
where = loc.readable(directions)
print "You can go " + ", ".join(where[:-1])\
+ ", or " + where[-1]
else:
print "You cannot go there."
You will notice that the more modular code is also easier to extend. For example, the inventory can now hold more things than gold, and it's easy to add new commands to query for weapons, potions, etc. Furthermore, it somewhat separates the code from the data, making it less cumbersome and error-prone to add new locations and actions.
Next up, define class Object with subclasses for animate objects, objects you can pick up, immobile objects, etc; and populate the locations with instances of these. Different subclasses can have different interactions defined, and inherit from more basic superclasses which implement fundamentals like take, drop, kill, etc.
What to map into objects is a broad topic, but a few simple guidelines would be to isolate and encapsulate unrelated code into their own classes, and make them as decoupled as possible (code implementing "location" should not need to know pretty much anything about code in "inventory", and vice versa).
Your code for this has some serious structural problems. If I understand correctly, you're trying to accept repeated commands and execute some code to make them function the way you intend.
The problem is that your function to run the game is recursive, so every time you execute a command other than 1, 2, 3, or 4, you're calling your function again without returning from the first one. Eventually, if you enter enough commands, you'll get an error saying that you're recursing too deeply and the game will error out.
What you want is something more like this:
def prompt():
x = input('Type a command: ')
return x
def ProcessAction(command):
if command == '1':
print()
elif command == '2':
print()
elif command == '3':
print()
elif command == '4':
print()
elif command == 'geld': #Actions start here
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
elif command == 'inv':
if not inv:
print("\n\tYou don't have any items..\n")
else:
print('\n\t' + str(inv) + '\n')
#Actions end here
curr_command = None
while curr_command not in ("1", "2", "3", "4"):
curr_command = prompt()
ProcessAction(curr_command)
What this will do is keep asking for new commands and processing them until one of the commands that exits the game is entered.
Edit: From your comment below, it sounds like you're trying to figure out how to display gold and inventory every time a command is entered without requiring a special command to do it. If this is what you're after, you can add print statements to the while loop above to ensure that it's printed before every prompt. In that case, the while loop might look like:
while curr_command not in ("1", "2", "3", "4"):
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
if not inv:
print("\n\tYou don't have any items..\n")
else:
print('\n\t' + str(inv) + '\n')
curr_command = prompt()
ProcessAction(curr_command)
Hope that gets closer to what you're after.
Edit 2: Ok, after reading the full code for your game, I think you might want to consider reorganizing the entire thing. Think about what you want the game to be: the player enters a series of commands and each command does two things, it changes the game's state and it prints out a response based on both the current state and the new state.
So, you should think about processing your commands with a loop like I described. Then, fold all those different functions into ONE ProcessAction(command) function that figures out from the game's state (which you store in variables) what to print out and how to change the state.
If it's a game where you're going room to room, for example, you might keep a global variable room that defines where you are. Your ProcessAction function then follows logic that says "If I'm in room A and the character types this thing then print out B and change room to C, and sets gold to 0."
To make this work well, you'll have to step back and think about the overall "story" of your game, how to store the state in various variables, and how to make your ONE ProcessAction function handle all the possible states and commands that can be issued.
Doing this puts you on the path of developing what's called a "state machine," where you have a simple, general function that looks at a data structure (probably some nested dicts) that you fill up with what each command does when your game is in each state and where to go next, as well as what to print out.
This Wikipedia article describes the concept of a state machine. How you implement it in Python is up to you. I can tell you that if you're careful you should be able to do this without repeating any code. http://en.wikipedia.org/wiki/State_machine
Another edit: Answering a question you placed in the comments below, if you think you have to print out, for example, the value of a player's gold in multiple places, you can do something like this:
def print_gold(gold_value):
print('\n\tYou have ' + str(gold_value) + ' euro. RICH BOY BRO!.\n')
print()
then use print_gold(gold) in place of those print statements whenever you need to do that. However, I think you may want to take a step back and think about rewriting the whole thing with some of the thoughts I've offered before you tackle that problem.
My earlier answer is long and addresses a number of problems in the OP's code, but he's asking about one specific thing, so I thought I'd separate out that answer here.
If you have some code you'd like to repeat multiple times, you might be tempted to copy and paste it around in your code In your case, that would be something like:
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
Wrapping that in a function will allow you to execute that same code anytime you need to do that thing, without copying and pasting. You're already defining functions, so you seem to have the concept down, but this wrapped in a function might look like:
def print_gold(gold_value):
print('\n\tYou have ' + str(gold_value) + ' euro. RICH BOY BRO!.\n')
print()
With this function defined, anytime you place print_gold(gold) in your code, it'll pass the value of gold into that function as the variable gold_value and print it out as you've specified.
So, if for some reason you had code that looked like this:
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
you could turn this into:
def print_gold(gold_value):
print('\n\tYou have ' + str(gold_value) + ' euro. RICH BOY BRO!.\n')
print()
... somewhere else in your code ...
print_gold(gold)
print_gold(gold)
print_gold(gold)
Those three lines are function calls, which tell Python to execute the function you've defined with def.
I am assuming you are currently pasting something into this prompt everytime you want to run your Python program, and your question is about how to avoid that:
The answer to this problem is generally to save your program's code in a whatever.py file and run that file... either by doubleclick or through the terminal. The exact instructions depend on your operating system etc.
If I understood correctly the latest edit to your question, then you might want something like:
def get_gold_or_inv(command):
if command == 'geld': #Actions start here
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
elif command == 'inv':
if not inv:
print("\n\tYou don't have any items..\n")
else:
print('\n\t' + str(inv) + '\n')
while True:
command = prompt()
if command == '1':
print()
elif command == '2':
print()
elif command == '3':
print()
elif command == '4':
print()
get_gold_or_inv(command)
Are you trying to say that you want this to run in an infinite loop until quit?
You can do that either by encapsulating the entire function block with a while True: or just by calling AlleenThuis() after the ifs and elifs. I took the liberty to rewrite your implementation using the first option below.
def AlleenThuis():
while True:
command = prompt()
if command == '1':
print()
elif command == '2':
print()
elif command == '3':
print()
elif command == '4':
print()
elif command == 'geld': #Actions start here
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
elif command == 'inv':
if not inv:
print("\n\tYou don't have any items..\n")
else:
print('\n\t' + str(inv) + '\n')
else:
print("Invalid command")
Hope I didn't misunderstand you.
EDIT:
def action(cmd):
if cmd == 'geld':
print('\n\tYou have ' + str(gold) + ' euro. RICH BOY BRO!.\n')
print()
elif cmd == 'inv':
if not inv:
print("\n\tYou don't have any items..\n")
else:
print('\n\t' + str(inv) + '\n')
else:
# If the command was not found
return False
# If the command was found and the else statement was not run.
return True
Then just run this at the start of every scenario if action(command): return ScenarioName().
Hope that helps!

Categories

Resources