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.
Related
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.
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.
I am trying to create a python program to save my friends' birthdays and access them easily and check for birthdays each day(I am not great at remembering dates and I never use facebook), but when I add a new birthday it is only accessible until I end the program - it then disappears again. I have been struggling with this for a while now and would really appreciate help fixing the error. Thanks!
import time
import pickle
def main():
birthday_file = open('birthdays_dict.dat','ab')
birthday_doc = open('birthdays_dict.dat','rb')
birthdays = pickle.load(birthday_doc)
date = time.strftime("%m/%d")
again = 'y'
while again.lower() == 'y' or again.lower() == 'yes':
choice = menu_choice()
if choice == 1:
name = add_name()
birthday = add_birthday()
birthdays[name] = birthday
print(name)
print(birthday)
pickle.dump(birthdays,birthday_file)
elif choice == 2:
print('Birthdays today(' + date + '):')
birth_today = {}
for key, value in birthdays.items():
if value == date:
print(key)
elif choice == 3:
search_name = input('Enter name to search: ')
print()
if search_name in birthdays:
print(birthdays[search_name])
if birthdays[search_name] == date:
print('Their birthday is today!')
else:
print('Not found')
else:
print('Not a valid selection!')
print()
again = go_again()
birthday_file.close()
birthday_doc.close()
Your problem is that you keep appending new dicts onto the file instead of replacing the old one, but then at startup you only load the very first one instead of all of them.
To fix this, you need to change this:
birthday_file = open('birthdays_dict.dat','ab')
… to this:
birthday_file = open('birthdays_dict.dat','wb')
But don't do that change on its own, because that will erase the file before you've read the old version!
You probably want to do something like this at the top of the function:
with open('birthdays_dict.dat', 'rb') as birthday_doc:
birthdays = pickle.load(birthday_doc)
I used a with statement so the file will automatically get closed right after the load, so it's definitely safe for us to overwrite it later.
Then later, when you want to write to the file, that's when you open it in w mode to erase the file and overwrite it with the new version—at which point you might as well close it immediately, because if you ever do write to it again, you're going to want to erase it again first, so let's use with again:
with open('birthdays_dict.dat', 'wb') as birthday_doc:
pickle.dump(birthdays, birthday_doc)
So I am working on doing a "simple" task since like 2h and still can't find the solution, so where is my question :
I want to search in a file, line by line, and if no result is found, at the end print something, else call a function.
def DeletItemCheckUp():
import re
find = True
itemNumber = input("\n what is the item you want to delet : ")
fileItem = open('Data_Item', 'r', encoding='Utf-8')
for line in fileItem:
sr = re.search(r'^\b%s\b'%itemNumber,(line.split(';')[0]))
if (sr == None):
pass
print("This item don't exist.")
fileItem.close()
if (find == True):
return itemNumber
DeletItem()
so here is the problem I have got with different try :
1. Print "This item don't exist." for every line that didn't had my itemNumber.
2. When there was actually no match found, its would not call DeletItem().
objectif of the code :
Ask for a item to delet, check in a file if the unique item number exist, if so, call DeletItem() to delet it, else, tell the user that this unique item number don't exist.
Few overlooks in there to achieve what you ask. We are going to use a flag (true/false) to know when we found something, and based on that we will decide whether to call the function or print/return the number.
def DeletItemCheckUp():
import re
find = False # initialize to False
itemNumber = input("\n what is the item you want to delet : ")
fileItem = open('Data_Item', 'r', encoding='Utf-8')
for line in fileItem:
sr = re.search(r'^\b%s\b'%itemNumber,(line.split(';')[0]))
if (sr == None):
continue # do nothing and continue
else:
# we found the number, set the flag and break
find = True
break # no need to continue searching
fileItem.close()
if (find):
DeletItem() # call the function
else:
print("This item don't exist.")
1) replace the pass with your print('This item doesn't exist'). "Pass" means "do nothing."
2) Your DeleteItem() is after the return. Nothing executes after the return because you have returned to the place the function was called from. You want
else:
DeleteItem()
I am new to python (and programming in general) and am making a database/register for a typical class. I wanted the user to be able to add and remove pupils from the database, I used lists primarily for this but have hit a stump.
Whenever I restart the program the list the user has modified returns back to the defualt list I specified in the code. I looked around the internet and tried to save the list onto a seperate txt file. However the txt file also goes back to the defualt every time I restart the program. I would like you to please give me a way to save the changes made to the list and keep them that way. Here is the code (it's not very good):
def menu():
print "*****************CLASS REGISTER*****************"
print "Press 1 See The List Of Pupils"
print "Press 2 To Add New Pupils"
print "Press 3 To Remove Pupils"
print "Press 0 To Quit \n"
filename = open('pupil.txt','r')
pupil = ["James Steele", "Blain Krontick", "Leeroy Jenkins", "Tanvir Choudrey"]
def see_list(x):
print x
def add_pupil(x):
print "You have chosen to add a new pupil.\n"
option = raw_input("Please type the childs name.")
x.append(option)
filename = open('pupil.txt','w')
filename.write('\n'.join(pupil))
filename.close()
print option, "has been added to the system."
return x
def delete_pupil(x):
print "You have chosen to remove a pupil.\n"
option = raw_input("Please type the childs name.")
if option in x:
x.remove(option)
filename = open('pupil.txt','w')
filename.write('\n'.join(pupil))
filename.close()
print option, "has been removed from the system."
else:
print "That person is not in the system."
return x
one = 1
while one != 0:
menu()
option = input()
if option == 1:
see_list(pupil)
elif option == 2:
add_pupil(pupil)
elif option == 3:
delete_pupil(pupil)
elif option == 0:
break
else:
print "That is not a valible choice."
filename = open('pupil.txt','w')
filename.write('\n'.join(pupil))
filename.close()
if option == 0:
quit
Well, you just open the pupil.txt file but never read back its contents. You need something like this:
filename = open('pupil.txt', 'r')
contents = filename.read()
filename.close()
pupil = [name for name in contents.split('\n') if name]
Also, you will need to handle the case when the pupil.txt file does not exist; this can be done with a try..except block around the IO calls.
Finally, as one of the comments has mentioned above, have a look at the pickle module, which lets you store a Python object in a file in Python's internal format (which is not really readable, but saves you a lot of hassle).
Not related to your question directly, but this:
one = 1
while one != 0:
...
is silly. All you need is:
while True:
...
This is what a database is for. Use sqlite - a simple file-based database the libraries for which come bundled with python.