Accessing and array and nested properties in Python - python

Sorry, I have searched and not managed to figure out an answer to this - I know there are lots of threads and questions in relation to it but cannot see an answer. Please redirect me or perhaps suggest a solution! TIA
I have an array of objects.
One (4 actually) of the properties of the objects in this array is another class.
Input taken from the user refers to one of the properties in the array which contains an instance of the other class. I simply want to read that data.
class direction():
dest = -1
lock = ''
class room():
roomname = ''
desc = ''
n = direction()
s = direction()
w = direction()
e = direction()
item = ''
rooms = []
rooms.append( room() )
rooms.append( room() )
rooms.append( room() )
rooms.append( room() )
rooms.append( room() )
rooms[0].roomname = 'outside'
rooms[0].desc = ''
rooms[0].n.dest = 'bathroom'
rooms[0].item = ''
rooms[1].roomname = 'hall'
rooms[1].desc = 'The hallway has doors to the east and south'
rooms[1].s.dest = 2
rooms[1].e.dest = 3
rooms[1].item = ''
and so on..
Now I take input from the user eg: "go n"
and would like to check/read the 'lock' property from the property that relates to the taken direction in the current room. currentRoom is an integer that relates to the LIST element that it links to.
Please do not criticise my lack of constructors. I am trying to keep the code as simple as possible initially and will introduce those later on.
I take input as follows:
print('Your action:')
move = input('>>>').lower().split()
I use the following line of code to check that the taken direction exists in the current room as follows:
if getattr(rooms[currentRoom], move[1]) != '':
and then want to check the lock property for the given direction in the current room. Something like this... (which does not work)
if rooms[currentRoom].move[1].lock != '':
I hope this is clear enough! Many thanks.

if rooms[currentRoom].move[1].lock != '':
This line of code will not work because move[1] is a string. If you wanted to access the variable in the class you would have to call it in the line of code
if rooms[currentRoom].n.lock != '':
A solution would be to get the value stored in the object then as follows
x = getattr(rooms[currentRoom], move[1]) #getattr() returns direction() object
if x.lock != '': #check lock value in direction() object

Related

How to refactor while loop with too much if, elif statement to function

I'm new here and I have a problem with too much if, else statement in while loop. I want to refactor it to function, but I don't have any idea how to do it.
My code:
brand = input("Please select a brand...")
if brand.lower() == "XX" or sex == "1":
print("You selected a XX...")
while True:
product = input()
if product.lower() == "apple" or product == "1":
print("You selected Apples!\n")
while True:
size_schema = input()
if size_schema.lower() == "in" or size_schema.lower() == "inch" or size_schema == "1":
while True:
apple_size = float(input())
if 8.5 <= apple_size <= 12.0:
real_apple_size = round(apple_size, 2)
print("Your apple size is {} inch!".format(real_apple_size))
cursor = size_guide.find({})
for document in cursor:
a = document['Product']['Apple']['INCH']
try:
b = [float(x) for x in a if x != '']
result = min(enumerate(b), key=lambda i: abs(i[1] -
float(real_apple_size)))
c = str(result[1])
except ValueError:
pass
real_apple_size = str(real_apple_size)
if real_apple_size in document['Product']['Apple']['INCH']:
index = document['Product']['Apple']['INCH'].index(real_apple_size)
print("We have this apples from {} brand!"
.format(document['Brand']))
elif c in document['Product']['Apple']['INCH']:
last_list_value = next(s for s in reversed(a) if s)
index = document['Product']['Apple']['INCH'].index(c)
real_apple_size = float(real_apple_size)
print("SORRY! We don't have exactly your size, "
"but we have similar size from {} brand!"
.format(document['Brand']))
else:
print("Sorry, We don't have apples for you from {} brand!"
"Check our other products!"
.format(document['Brand']))
else:
print("Please select your apple size in range 8.5-12.0 inch!")
continue
break
I want to reduce this code and insert it in function.
Better (though probably not best) functional code would be a set of functions that are reusable, and each do one (or a very small number) of things. For example:
def get_product():
brand=input("What brand?")
#input validation logic
product=input("What product?")
#input validation for product given brand
size=input("What size?")
#input validation given brand and product
color=input("What color? (enter 'none' for no color)")
#That's right, more validation
return brand, prod, size, color
def prod_lookup(brand, prod, size, color):
cursor = size_guide.find({})
for document in cursor:
#lookup product with logic as in your post
if __name__ == "__main__":
brand, prod, size, color = get_product()
prod_lookup(brand, prod, size, color)
Again, this is just an example of one way to do it that would be much less messy. If you need to update your list of available products, for example, you only have to adjust one part of one function, rather than choosing from a deeply nested bunch of conditionals and loops.
I'm sure there are better ways, but hopefully this gives you some idea where to start thinking.
Adding one possible implementation of input validation with product lookup. This way, your brand will always be the product number rather than the string, which is usually a faster lookup:
brand_dict={'xx':'1','yy':'2'}
while True:
brand=input("Enter brand: ").lower()
if brand in brand_dict.keys():
brand=int(brand_dict[brand])
break
elif brand in brand_dict.values():
brand=int(brand)
break
else:
print("Brand not recognized. Try again!")
First, just wrap the whole thing in one function
def foo():
brand = input("Please select a brand...")
if brand.lower() == "XX" or sex == "1":
# etc.
Now, note that your first if statement encompasses the rest of the function, and there is no else clause. That is, if the condition fails, you'll fall through to the end of the function and implicitly return. So just return explicitly if the condition does not* hold. This lets you immediately dedent the bulk of your code.
def foo():
brand = input("Please select a brand...")
if brand.lower() != "XX" and sex != "1":
return
print("You selected a XX...")
# etc
Repeat this process, either returning or breaking out of the enclosing infinite loop, for each of your else-less if statements.

making lists in a dictionary by user inputs

I'am a newbee in python and coding, and I really don't understand why my program isn't working correctly. The expected effect is "name wants to go to [destinations]" while my code doesn't distinguish different "{names : [destinations]}" pairs.
Here's my code, thanks for your attention here :)
response = {}
destinations = []
ptname = 'name pls.'
prompt = 'input some places in the world, input next to go to the next user'
active = True
unfinished = True
while active:
unfinished = True
name = input(ptname)
while unfinished:
destination = input(prompt)
if destination != 'next':
destinations.append(destination)
print(destination + ' has been added to your list.')
response[name] = destinations
elif destination == 'next':
unfinished = False
go_on = input('wish to continue? y/n')
if go_on == 'n':
active = False
print(response)
for name, destinations in response.items():
print(name + ' wants to go to ')
for destination in destinations:
print(destination)
you have to reset (actually create a new reference) for destinations in the same outer loop where you set name or you're be reusing the same list for all names.
name = input(ptname)
destinations = [] # create new reference for the list
while unfinished:
...

check existence of data in python dictionary

I have a program that maintains a flat file database of cd information. I am trying to write a function that updates the database. In this function I am checking to see if the artist exists and if so, appending the album name to this artist, but for some reason it will not see that the artist I type in already exists. I made sure that I type it in exactly like it is in the dictionary but for some reason python will not see that it is there. Why would this be happening? I have included sample input as well as the python program. Any help would be greatly appreciated.
import sys
def add(data, block):
artist = block[0]
album = block[1]
songs = block[2:]
if artist in data:
data[artist][album] = songs
else:
data[artist] = {album: songs}
return data
def parseData():
global data
file='testdata.txt'
data = {}
with open(file) as f:
block = []
for line in f:
line = line.strip()
if line == '':
data = add(data, block)
block = []
else:
block.append(line)
data = add(data, block)
return data
def artistQry():
global artists, usrChoiceArt, albums, usrChoiceAlb, usrArtist
artists=sorted(data.keys())
for i in range(0,len(artists)) :
print str(i+1) + " : " + artists[i]
usrChoiceArt = raw_input("Please choose an artist or enter q to quit:")
if usrChoiceArt=='q' :
print "Quitting Now"
exit()
else :
albumQry()
def albumQry():
global artists, usrChoiceArt, albums, usrChoiceAlb, usrArtist
usrArtist=artists[int(usrChoiceArt)-1]
albums=sorted(data[usrArtist].keys())
for i in range(0,len(albums)) :
print str(i+1) + " : " + albums[i]
usrChoiceAlb=raw_input("Please choose an album or enter a to go back:")
if usrChoiceAlb=="a":
artistQry()
else:
trackQry()
def trackQry():
global artists, usrChoiceArt, albums, usrChoiceAlb, usrArtist
usrAlbum=albums[int(usrChoiceAlb)-1]
tracks=data[usrArtist][usrAlbum]
for i in range(0,len(tracks)) :
print tracks[i]
usrChoiceTrack=raw_input("Enter \"a\" to go back or \"q\" to quit:")
if usrChoiceAlb=="q":
print "Quitting Now"
exit()
elif usrChoiceTrack=="a":
albumQry()
else:
print "Invalid Choice"
trackQry()
def artistExist(Name):
for i in range(0,len(data.keys())):
if Name==data.keys()[i]:
return True
else:
return False
def updData():
artistName=raw_input("Please enter an artist name:")
albumName=raw_input("Please enter an album name:")
trackList=raw_input("Please enter the track list seperated by comma's:")
if artistExist(artistName):
data[artistName].append(albumName)
print data[artistName]
elif not artistExist(artistName):
print "Quitting"
exit()
if __name__ == '__main__':
data = parseData()
if sys.argv[1]=='-l':
artistQry()
elif sys.argv[1]=='-a':
updData()
Input data:
Bob Dylan
1966 Blonde on Blonde
-Rainy Day Women #12 & 35
-Pledging My Time
-Visions of Johanna
-One of Us Must Know (Sooner or Later)
-I Want You
-Stuck Inside of Mobile with the Memphis Blues Again
-Leopard-Skin Pill-Box Hat
-Just Like a Woman
-Most Likely You Go Your Way (And I'll Go Mine)
-Temporary Like Achilles
-Absolutely Sweet Marie
-4th Time Around
-Obviously 5 Believers
-Sad Eyed Lady of the Lowlands
In your function artistExist, you return False on the very first iteration! Instead, you must wait until all iterations are finished.
for i in range(0,len(data.keys())):
if Name==data.keys()[i]:
return True
return False
In addition to what Padraic Cunningham says below, the elif here is also redundant:
if artistExist(artistName):
...
elif not artistExist(artistName):
...
If something isn't True, then it can only be False. So really you should just have
if artistExist(artistName):
...
else:
...
And since the function is just a needless one-liner, an even better expression is
if artistName in data:
...
else:
...
Apart from returning too early by returning False in the loop you are doing way too much work, you simply need to use return Name in data:
def artistExist(Name):
return Name in data # will return True or False with O(1) lookup
Every time you call .keys you are creating a list in python2 so your lookup is actually quadratic in the worst case as opposed to 0(1) with the simple return Name in data. A big part of using a dict is efficient lookups which you lose calling .keys. If you actually wanted to iterate over the keys you would simply for key in data, no call to .keys and no need for range.

Save File Function

So for my intro programming class we have to create a game with a save/load function and I'm trying to test out some code to make sure it works.
For some reason I cannot get the following function to work properly. I've tried going through it line by line in the Idle and it works just fine there but once I try to use the same system in a function it just will not work. Help please?
def save(name,inventory,mapGrid,x,y,enemy):`
choice = 0
file = shelve.open("save_files")
save = {'save1':file['save1'],'save2':file['save2'],'save3':file['save3']}
print("Where would you like to save?")
print("Save 1 -", save['save1']['name'])
print("Save 2 -", save['save2']['name'])
print("Save 3 -", save['save3']['name'])
choice = input("Enter Number:\t")
if choice == 1:
save['save1']['name'] = name
save['save1']['inventory'] = inventory
save['save1']['mapGrid'] = mapGrid
save['save1']['x'] = x
save['save1']['y'] = y
save['save1']['enemy'] = enemy
file['save1'] = save['save1']
file.sync()
if choice == 2:
save['save2']['name'] = name
save['save2']['inventory'] = inventory
save['save2']['mapGrid'] = mapGrid
save['save2']['x'] = x
save['save2']['y'] = y
save['save2']['enemy'] = enemy
file['save2'] = save['save2']
file.sync()
if choice == 3:
save['save3']['name'] = name
save['save3']['inventory'] = inventory
save['save3']['mapGrid'] = mapGrid
save['save3']['x'] = x
save['save3']['y'] = y
save['save3']['enemy'] = enemy
file['save3'] = save['save3']
file.sync()
file.close()
print("Game Saved")
EDIT: After running the function it should save the dictionary to file['save#'] and allow me to access the data later on, but the data doesn't save to the shelve file and when I try to access it again there's nothing there. ((Sorry should have put this in right off the bat))
For example if I run the save() function again it should display the name associated with the save file, but it just shows 'EMPTY'.
The basic thing I have the save_files set to is
file['save#'] = {'name':'EMPTY'}
Since your if statements are comparing int, make sure that choice is also an integer. It's possible that choice is actually a string, in which case none of the comparisons will be True. Basically:
choice = int(input("Enter Number:\t"))
Alternatively you could change all comparisons to strings, but the important thing is to assure type consistency in the comparisons

Python 2 game coordinates class

I wanted to create a x-y coordinate system even though this is supposed to be a text RPG as to keep track of where everything is. So, I was experimenting on making a function and test for that function that would let the character move on a x-y grid, however, no matter what I try, I cannot make it work. Here is the code:
class Player:
def movement(charactor_movement):
proceed = 0
if charactor_movement == "left":
character.position_x = character.position_x - 1
proceed = 1
elif charactor_movement == "right":
character.position_x = character.position_x + 1
proceed = 1
elif charactor_movement == "forward":
character.position_y = character.position_y + 1
proceed = 1
elif charactor_movement == "backward" or charactor_movement == "back":
character.position_y = character.position_y - 1
proceed = 1
charactor = Player()
charactor.position_x = 0
charactor.position_y = 0
proceed = 0
while proceed == 0:
print "You are at",
print charactor.position_x,
print"x and",
print charactor.position_y,
print"y."
global charactor_movement
charactor_movement = raw_input("Where are you going?")
charactor.movement()
At this point, it does what it is supposed to do up to changing the coordinates, as it prints "You are at 0 x and 0 y" and "Where are you going?" no matter what I type. I have tried adding an else to the function which it defaulted to no matter what I typed and gave me "Sorry, I cannot understand you." Any comments on fixing or generally improving the code would be appreciated.(Note: For the testing I purposely did not add a way to exit. The class is what i need fixed.)
You are getting the same coordinates with each iteration because your values within your while loop are not changing. Incrementing character.position_x within movement will never change the value of character.position_x within your while loop, as it is outside your function's scope. You have to use the global keyword within your movement function for each variable you are changing should you want your current logic to remain the same. Additionally, why not just pass charactor_movement as a parameter to your movement function, as opposed to using global as you currently are doing.
A minimal example:
Consider the following:
def somefunct(x):
mycode = x
mycode = 'no codez'
while True:
print mycode
codez = raw_input('gimme teh codez: ')
somefunct(codez)
which outputs
>>>[evaluate untitled-1.py]
no codez
gimme teh codez: codez!
no codez
Declaring mycode as global in the function places it in the scope of the while loop when assigned, thus
def somefunct(x):
global mycode #make variable global here
mycode = x
mycode = 'no codez'
while True:
print mycode
codez = raw_input('gimme teh codez: ')
somefunct(codez)
results in the output
>>>[evaluate untitled-1.py]
no codez
gimme teh codez: codez!
codez!

Categories

Resources