Dictionary value is not changed inside a for loop assignment - python

So I am learning python using Learn python the hard way. I am trying to develop a inventory system. The goal is to build this into a class that will be pulled by the room class. I am going to use a dictionary for the inventory so that when I create the inventory for each room I can say where they are in the room.
So in the bedroom I can say that there is a knife on the bed rather than just saying you see knife, candle, oil.
Right now I am working on picking up the item when someone says take knife. I am able to do a weird search and set that value to None so that when someone looks in the room it does not show up but I seem to be running into a scope issue. I read multiple other questions on this site that said since its a mutable object i don't need to do Global dict and when i tried that it got an error. I am able to edit the object but when i go back out of my if statement and for loop it won't carry over.
Please help me :)
#This is the inventory in this room
inventory = {'desk': 'Miniature Fusion Warhead',
'bed':'knife', 'sink':None}
def take(item):
if item in inventory.itervalues():
#add_item_to_inventory(item)
for key, value in inventory.iteritems():
if value == item:
print ("Wow.\n"
"Look at you.\n"
"Picking shit up and everything.")
value = None
else:
print ("What do you think this is?"
" The dollar store? We don't "
"got that shit here!")
# Prints out what items are in the room.
# I am hoping to later come up with an idea for how
# To make different variations of this, and have it randomly
# pick between the different ways it says what it sees.
for key, value in inventory.iteritems():
if value != None:
print "You see a %s on the %s." % (value, key)
print "What do you want to pick up?"
ui = raw_input(">")
split_ui = ui.split()
print split_ui
if len(split_ui) > 1:
if split_ui[0] == "take":
print ("You reach over to grab the %s."
"\n...") % split_ui[1]
take(split_ui[1])
else:
print ("What you talking bout Willis? "
"Don't you know this is just about "
"takin shit.")
else:
print ("Who taught you how to talk?"
"\n...\nLet me show you how its done.\n"
"Use a verb, like take, then throw in an "
"object like knife.")
print inventory
This is the output that I am given.
You see a knife on the bed.
You see a Miniature Fusion Warhead on the desk.
What do you want to pick up?
>take knife
['take', 'knife']
You reach over to grab the knife.
...
Wow.
Look at you.
Picking shit up and everything.
{'sink': None, 'bed': 'knife', 'desk': 'Miniature Fusion Warhead'}
Important note: This currently only works if you take the knife and not the Warhead. I need to figure out another solution for items with multiple words.
Thanks!

The value inside your loop is different than the real value of the dictionary.
It is just a reference to that value, so when you do value = None you actually change
the value of the reference to hold the new value None and not the value of the dictionary.
To demonstrate it better this is before the assignment inside the for key, value in inventory.iteritems():
------- -------------------------
|value| -------> |value of the dictionary|
------- -------------------------
this is after value = None
-------------------------
|value of the dictionary|
-------------------------
------- ------
|value| -------> |None|
------- ------
As you can see the dictionary value does not change. Only the variable value of the for loop
changes. This variable belongs to the scope of the for loop and after that it is discarded.
An alternative would be instead of value = None to do:
inventory[key] = None

zbs is correct, you're only changing the value of the pointer to the dict value. However, you're making this way too hard:
#This is the inventory in this room
inventory = {'Miniature Fusion Warhead': 'desk',
'knife':'bed'}
player_inventory = set()
def take(item):
if item in inventory:
print("You picked up the {}".format(item))
player_inventory.add(item)
del inventory[item]
else:
print("That item doesn't exist")
while True:
print('')
print("Inventory: " + ', '.join(player_inventory))
for k,v in inventory.items():
print("You see a {} on the {}".format(k, v))
print("What do you want to pick up?")
ui = raw_input("> ").split()
verb = ui[0]
item = ' '.join(ui[1:])
if verb == 'take':
if item:
print("You take the {}".format(item))
take(item)
else:
print("That item doesn't exist")
else:
print("That's not an action")

Related

Variable inside dictionary value is unchanged even though same variable outside dictionary is changed. Why?

I have a dictionary restaurants and want to change the value of opening hours and closing hours in a nested list in restaurants depending on the day inputted by the user.
opening_time=0
closing_time=0
##snippet of the dictionary
restaurants={"Macdonald's":\
\
[{"Monday":[700,2400],\
"Tuesday":[700,2400],\
"Wednesday":[700,2400],\
"Thursday":[700,2400],\
"Friday":[700,2400],\
"Saturday":[700,2400],\
"Sunday":[1000,2200]},\
\
"Block XXX, #01-XX",\
"Fast food restaurant known for its all round excellent menu.",\
\
["Breakfast",[opening_time,1100],\
{"Egg McMuffin":"$1",\
"Hotcakes":"$1",\
"Big Breakfast":"$1"}],\
["Lunch/Dinner",[1100,closing_time],\
{"Double Cheeseburger":"$3.20",\
"McChicken":"$3.95",\
"Big Mac":"$4.70",\
"Chicken McNuggets (6pcs)":"$4.95",\
"McWings":"$4.95"}],\
["All Day",[opening_time,closing_time],\
{"Fillet-O-Fish":"$4.60",\
"Corn Cup":"$1.95"}]]}
I want the code to loop through and print all the restaurants and menus while indicating whether said restaurants and menus would be available for a user inputted time.
for key in restaurants: #key refers to restaurant name
print("","",sep='\n')
if day_now in restaurants.get(key)[0].keys(): #check if restaurant is open on that day
opening_time=restaurants.get(key)[0][day_now][0] #set opening and closing hours to those on that day
closing_time=restaurants.get(key)[0][day_now][1]
if time_now>=opening_time and time_now<closing_time: #check if restaurant is open within that time period
status="Open"
open_restaurants.update(restaurants)
print(key,"Status: "+status,"Opening Hours Today:"+str(opening_time)+" to "+str(closing_time),\
"Location: "+restaurants.get(key)[1],"Description: "+restaurants.get(key)[2],sep='\n')
for i in range(3, len(restaurants.get(key))): #goes through the menus the restaurant has
print(restaurants.get(key)[i][1][0]) #prints 0
print(restaurants.get(key)[i][1][1]) #prints 0
if time_now>=restaurants.get(key)[i][1][0] and time_now<restaurants.get(key)[i][1][1]: #check if menu have
print("")
print(restaurants.get(key)[i][0]+" Menu: Available","Item: Cost:",sep='\n')
for item in restaurants.get(key)[i][2].keys():
print(item, restaurants.get(key)[i][2][item],sep=' ')
else:
print("")
print(restaurants.get(key)[i][0]+" Menu: Unavailable","Item: Cost:", sep='\n')
for item in restaurants.get(key)[i][2].keys():
print(item, restaurants.get(key)[i][2][item],sep=' ')
else:
closed_restaurants.update(restaurants)
status="Closed"
print(key,"Status: "+status,"Opening Hours Today:"+str(opening_time)+" to "+str(closing_time),\
"Location: "+restaurants.get(key)[1],"Description: "+restaurants.get(key)[2], sep='\n')
else:
closed_restaurants.update(restaurants)
status="Closed"
print(key,"Status: "+status,"Opening Hours Today:"+str(opening_time)+" to "+str(closing_time),\
"Location: "+restaurants.get(key)[1],"Description: "+restaurants.get(key)[2], sep='\n')
print(opening_time) #prints the correct opening and closing hours
print(closing_time)
However, the opening hours and closing hours variables in the dictionary could not be assigned to the desired values in the loop and remained as they were first assigned outside the loop.
Directly printing the variable names showed that the new values were assigned successfully.
Could someone help me with the issue here? Thanks.
Let's use this simple example to make clear where your assumptions are wrong:
var = 1
lst = [var]
var = 2
print(lst)
What should this print, [1] or [2]? It will print [1]. What you have here is not a list of variable references, it is a list of integers. You took the value var had, and put that into a list. A copy, if you want.
What about this?
a = 1
b = a
a = 2
Again, b is still 1 after this. You took what was written at the location where a is stored, and put it in the location where b is stored.
You need to actually update the values inside the dictionary, it is not enough to update opening_hours.

How to match input with elements in list/dictionary in Python3

I'm very new at coding, and I'm trying to create a shop list with items and prices on it.
That is, once typed in all the items, the function should calculate the sum and stop the moment you exceed the budget.
So I wrote something like:
def shoplist():
list={"apple":30, "orange":20, "milk":60......}
buy=str(input("What do you want to purchase?")
If buy in list:
While sum<=budget:
sum=sum+??
shoplist ()
I really don't know how to match the input of an item with the price in the list...
My first thought is to use 'if', but it's kinda impractical when you have more than 10 items on the list and random inputs.
I'm in desperate need of help....So any suggestions would be nice!! (or if you have a better solution and think me writing it this way is complete garbage... PLEASE let me know what those better solutions are😭😭😭
The code you post will not run in python. list is a builtin and should not be used for a variable name, and is doubly confusing since it refers to a dict object here. input() already returns a str so the cast has no effect. if and while should be lowercase, and there is no indentation, so we have no way of knowing the limits of those statements.
There are so many things wrong, take a look at this:
def shoplist(budget):
prices = {"apple":30, "orange":20, "milk":60}
# Initialise sum
sum = 0
while sum <= budget:
buy = input("What do you want to purchase?")
# Break out of the loop if the user hts <RETURN>
if not buy: break
if buy in prices:
sum += prices[buy] # This gets the price
else:
print("Invalid item", buy)
shoplist(142)
So what have I changed? The budget has to come from somewhere, so I pass it in as a parameter (142, I made that up). I initialise the sum to zero, and I moved the while loop to the outside.
Notice as well lots of whitespace - it makes the code easier to read and has no effect on performance.
Lots of improvements to make. The user should be shown a list of possible items and prices and also how much budget there is left for each purchase. Note as well that it is possible to go over budget since we might only have 30 in the budget but we can still buy milk (which is 60) - we need another check (if statement) in there!
I'll leave the improvements to you. Have fun!
Take a look at this as an example:
# this is a dictionary not a list
# be careful not using python reserved names as variable names
groceries = {
"apple":30,
"orange":20,
"milk":60
}
expenses = 0
budget = 100
cart = []
# while statements, as well as if statements are in lower letter
while expenses < budget:
# input always returns str, no need to cast
user_input = input("What do you want to purchase?")
if user_input not in groceries.keys():
print(f'{user_input} is not available!')
continue
if groceries[user_input] > budget - expenses:
print('You do not have enough budget to buy this')
user_input = input("Are you done shopping?Type 'y' if you are.")
if user_input == 'y':
break
continue
cart.append(user_input)
# this is how you add a number to anotherone
expenses += groceries[user_input]
print("Shopping cart full. You bought {} items and have {} left in your budget.".format(len(cart), budget-expenses))
I've made some changes to your code to make it work, with explanation including using comments indicated by the # symbol.
The two most important things are that all parentheses need to be closed:
fun((x, y) # broken
fun((x, y)) # not broken
and keywords in Python are all lowercase:
if, while, for, not # will work
If, While, For, Not # won't work
You might be confused by True and False, which probably should be lowercase. They've been that way so long that it's too late to change them now.
budget = 100 # You need to initialize variables before using them.
def shoplist():
prices = { # I re-named the price list from list to prices
'apple' : 30, # because list is a reserved keyword. You should only
'orange' : 20, # use the list keyword to initialize list objects.
'milk' : 60, # This type of object is called a dictionary.
} # The dots .... would have caused an error.
# In most programming languages, you need to close all braces ().
# I've renamed buy to item to make it clearer what that variable represents.
item = input('What do you want to purchase? ')
# Also, you don't need to cast the value of input to str;
# it's already a str.
if item in prices:
# If you need an int, you do have to cast from string to int.
count = int(input('How many? '))
cost = count*prices[item] # Access dictionary items using [].
if cost > budget:
print('You can\'t afford that many!')
else:
# You can put data into strings using the % symbol like so:
print('That\'ll be %i.' % cost) # Here %i indicates an int.
else:
print('We don\'t have %s in stock.' % item) # Here %s means str.
shoplist()
A lot of beginners post broken code on StackOverflow without saying that they're getting errors or what those errors are. It's always helpful to post the error messages. Let me know if you have more questions.

Specific example: Is it possible to aviod the use of a global variable?

I have the following program and the variable(dictionary) in question is player_info that stores player information (name and goals). In order to solve the error that results currently, I simply need to make player_info a global variable, but I was wondering if stackoverflow experts could suggest or discuss the possibility of alternate ways of solving this problem WITHOUT the use of global variables.
Code
#FOOTBALL COACH app
#The program allows a user to enter a number of players (their names and goals scored) and then search for a player, returning their average goals for the three matches
import sys
def main():
mainmenu()
def mainmenu():
print("=====WELCOME to the MAIN MENU=============")
print("""
1..........Add New Players & Goals
2..........Search by Players
3..........Quit
=========================================
""")
choice=int(input("Enter choice:"))
if choice==1:
addplayers()
elif choice==2:
searchplayer(player_info)
elif choice==3:
sys.exit()
else:
print("You must make a valid choice - 1, 2 or 3")
def addplayers():
player_info= {} #create a dictionary that stores the player name: player goals
num_players = int(input("Please enter number of players you wish to enter:"))
print ("You are entering %s players" %num_players)
player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']
for i in range(0,num_players):
player_name = input("Enter Player Name :")
player_info[player_name] = {}
for entry in player_data:
player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.
mainmenu()
def searchplayer():
print("===============SEARCH by player: Calculate average goals==================")
name = input("Player name : ")
if name in player_info.keys():
#print student_info
print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
else:
print("Please enter a valid player name:")
main()
As mentioned, I am aware that re-writing this in the addplayer() sub would fix the problem:
global player_info
player_info = {} #create a dictionary that stores the player name: player goals
...I am looking for ways to solve the problem WITHOUT the use of global variables.
Update:
One answer below using return player_info is what I would like to go with, but it doesn't quite work yet. Also, I need to return to the main menu each time a player is added, not quite sure how to do this, without a mainmenu call each time. Any suggestions? https://repl.it/JRl5/1
You can use return inside your function to avoid using global variables. A simple example is shown below:
def addplayers():
player_info= {}
name = input("Enter Name: ")
test = int(input("Enter a number: "))
player_info[name] = test
return player_info
player_info = addplayers()
If you then wanted to use this in another function you would just pass in the dictionary as an argument to that function:
def searchplayers(player_info):
print (player_info)
Note: An interesting answer on "Why are global variables evil?"
Edit:
Your addplayers() was calling mainmenu() which itself was being calling within mainmenu(). This is a recursive function and it might be best to avoid these unless there's a good reason for having it. I would put the contents of mainmenu inside a while loop until some condition is met. The complete code is shown below (I have removed the main function as it wasn't really doing anything):
def mainmenu():
stop = False
while stop == False:
print("=====WELCOME to the MAIN MENU=============")
print("""
1..........Add New Players & Goals
2..........Search by Players
3..........Quit
=========================================
""")
choice=int(input("Enter choice:"))
if choice==1:
player_info = addplayers()
elif choice==2:
searchplayer(player_info)
elif choice==3:
print ("Exit the main menu")
stop = True
else:
print("You must make a valid choice - 1, 2 or 3")
def addplayers():
player_info= {} #create a dictionary that stores the player name: player goals
num_players = int(input("Please enter number of players you wish to enter:"))
print ("You are entering %s players" %num_players)
player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']
for i in range(0,num_players):
player_name = input("Enter Player Name :")
player_info[player_name] = {}
for entry in player_data:
player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.
return player_info
def searchplayer(player_info):
print("===============SEARCH by player: Calculate average goals==================")
name = input("Player name : ")
if name in player_info.keys():
#print student_info
print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
else:
print("Please enter a valid player name:")
mainmenu()
Store everything related to the game in a data structure, for example a dictionary, and pass it along in all functions where it can be updated as needed. Write a function "newgame" that creates this structure and initialises it.
In a way, this is object-oriented programming without using Python's syntax for classes and objects. Probably, you will learn these later in your class / tutorial.
Firstly, it is always possible to avoid using global variables. Secondly, global variables are possibly a misnomer in Python; global stores the variable in the local globals, which is typically the local module. That avoids a large part of the problem languages like C have with globals, in that they collide; Python has a namespace per module. For a simple script, where there is only one context, that might be fine.
Another namespace you might use is that of a particular object, using a class. This might look like:
class Game:
def mainmenu(self,...):
self.addplayers()
def addplayers(self):
self.player_info = {}
With that sort of code, whoever instantiates Game can make multiple instances, each passed as self when used. This is in large part syntactic sugar for a similar form of mutable state passing:
def mainmenu():
state={}
addplayers(state)
def addplayers(state):
state['player_info'] = {}
For some forms of programming, immutable state is far preferable (in particular, multithreading where data is shared, or to keep a log where you can undo steps). That's done similarly but you make a new state for each call:
def mainmenu():
state = {}
state = addplayers(state)
def addplayers(oldstate):
newstate = oldstate.copy()
newstate['player_info'] = {}
return newstate
Python isn't designed for this and doesn't really have a mode to keep you from inadvertently modifying mutable types. Some types can be converted to similar types that are immutable, like frozenset or tuple.
One of the weirder hacks we can do is calling a Python function with a different set of globals than it normally has. This can be abused to take your existing functions, global statements and all, and have them use a different variable:
fakeglobals = mainmenu.__globals__.copy()
exec(addplayers.__code__, fakeglobals)
Your original code has calls back and forth between functions, though, and each of those will reset their globals per that __globals__ attribute.
Your code also implements a loop using tail recursion. This is not optimized in Python and will run out of stack space eventually. In languages where tail recursion is optimized, you can pass along state as arguments continously and need not return it.

Defining global variables to hold values that will be referenced by a raw_input

I am making a text based RPG. With that bit of context, let's say I wanted to make a global list of spells, and then define certain actions for those spells to take when called upon by a user. So if I have a global list of spells, what is the best way to assign certain actions for those spells to have? This is a bit of code that will help you somewhat understand my combat mechanics. I need to allow the user to call upon 'heal', for example, and then have heal hold the properties of raising health numbers and a cool down mechanic, e.g. "You must wait 5s to use this spell again." What's the best way to define those properties, and have those properties be global, so they can be referenced through each combat function without having to reiterate the properties?
Here is a portion of my code that may clear up my combat mechanics. When the player is prompted under raw_input 'inp' to type the name of the spell, and they type heal, a cool down effect should apply for a certain length of time, or perhaps a certain number of turns in combat, as well as the ability to raise user life. The property I use for life is under a class called 'user' and a variable 'health'. So user.health = 100 is valid
available_spells = ['heal', 'fireball']
equipped = {'Armor': 'Mage Robes',
'Weapon': 'Wooden Staff',
'Spells': ['heal', 'fireball']
print "Your available spell(s) is(are) '%s'. " % equipped["Spells"]
inp = raw_input("Type the name of a spell you want to use.: ").lower()
lst = [x for x in available_spells if x.startswith(inp)]
if len(lst) == 0:
print "No such spell"
print ' '
elif len(lst) == 1:
spell = lst[0]
print "You picked", spell
#combat functions here
else:
print "Which spell of", equipped["Spells"], "do you mean?"

(Python) For loop syntax - execute for only one item?

Pretty new to python/programming in general, this is my biggest project yet.
I am writing a program that will do SUVAT equations for you. (SUVAT equations are used to find the displacement, start/end velocity, acceleration and time travelled by an object with constant velocity, you may call them something different.)
I made this list:
variables = ["Displacement", "Start Velocity", "End Velocity", "Acceleration", "Time"]
which is used in the following while/for loop:
a = 0
while a==0:
for variable in variables:
# choice1 is what the user is looking to calculate
choice1 = raw_input("Welcome to Mattin's SVUVAT Simulator! Choose the value you are trying to find. You can pick from " + str(variables))
# will execute the following code when the for loop reaches an item that matches the raw_input
if choice1 == variable:
print "You chave chosen", choice1
variables.remove(variable) #Removes the chosen variable from the list, so the new list can be used later on
a = 1 # Ends the for loop by making the while loop false
# This part is so that the error message will not show when the raw_input does not match with the 4 items in the list the user has not chosen
else:
if choice1 == "Displacement":
pass
elif choice1 == "Start Velocity":
pass
elif choice1 == "End Velocity":
pass
elif choice1 == "Acceleration":
pass
# This error message will show if the input did not match any item in the list
else:
print "Sorry, I didn't understand that, try again. Make sure your spelling is correct (Case Sensitive), and that you did not inlcude the quotation marks."
Hopefully the comments I have written in the code should explain my intentions, if not, feel free to ask anything.
The problem is that when I run the code, and input choice1, the for loop activates the last line of code:
else:
print "Sorry, I didn't understand that, try again. Make sure your spelling is correct (Case Sensitive), and that you did not inlcude the quotation marks."
and then prompts me to enter the input again, and will do this as many times as it needs to get to the item on the list that I am typing.
However, I specifically coded that if what I input does not match the item on the list the for loop is currently checking, but does match one of the other items on the list, then it should pass and loop round to checking the next item.
I am probably doing something stupid, but I don't see it, so please help me figure out what I have to do to get my desired result? I assumed it was the syntax I had wrong so that is why that is the title.
Thanks for any help, I appreciate it.
Besides the problem with the indentation in your pasted code, I would rewrite it as such:
while True:
choice = raw_input('...')
if choice in variables:
print "You chave chosen", choice
# Remove the chosen member from the list
variables = [v for v in variables if v != choice]
# Break out of loop
break
# Print error messages etc.
Also remember that string comparisons are case sensitive. I.e 'Displacement' != 'displacement'.

Categories

Resources