I have been having trouble trying to delete an element or pair from my dictionary. It is supposed to delete both the father and the son when you type in the son's name, but it always produces this error:
Traceback (most recent call last):
File "C:\Users\jam7733\Desktop\whos ur daddy.py", line 23, in <module>
daddy()
File "C:\Users\jam7733\Desktop\whos ur daddy.py", line 20, in daddy
if del_son in fathers:
TypeError: unhashable type: 'list'
here is the code i have:
def daddy():
fathers = {'johnny':'john Dupuis','alex':'jordan belfort','henry':'daniel plainview','mike':'brian cranston','max':'fred man','benny':'nick flick','billy':'robert kardashian'}
choice = raw_input("do you want to: a)add a father-son pair,b)delete a pair, c)replace a pair,d)look up father-son pair, or e)look for grandfather")
if choice == 'd':
name = raw_input("what is the first name of the child? ")
if name in fathers:
print name,"is the child of",fathers[name]
else:
print "sorry, we do not have this name listed, please try again."
daddy()
if choice == "a":
new_dad = raw_input("what is the name of the new dad?")
new_son = raw_input("what is the name of the son?")
fathers[new_dad]=new_son
if choice == "b":
print"Here are the names of the fathers that you can delete: "
print fathers
del_son = raw_input("what father/son pair do you want to delete?(type first name of son").split(" ")
if del_son in fathers:
del fathers[del_son]
daddy()
As said in the comments by #kindall and #PadraicCunningham, del_son is a list:
del_son = raw_input("what father/son pair do you want to delete?(type first name of son").split(" ")
because here you split its name with a space. Given your code I believe this is a mistake, or there's something you don't understand when you wrote that line.
In python you cannot use a list as the key of a dict, because a dict need all its keys to be non-mutable, in order to ensure that a given key is unique. And in python, lists and dicts are mutable. That means that when you do:
i = 1
i = 2
i is only a reference to the int object instanced with the value 1. When you assign 2 to i, the former instance with value 1 is being forgotten about, and will be collected by the garbage collector. But when you do:
l = []
l.append(1)
l.append(2)
then you've made a reference from l to a list object, which you modified to contain two values: 1 and 2. So now, imagine that you want to index a value with a list in a dict, and you do:
d = {}
l1 = [3,1]
l2 = [3,4]
d[l1] = 'foo'
d[l2] = 'bar'
naively, there's no reason why that wouldn't work and be:
{[1,4]: 'foo', [3,4]: 'bar'}
but then what if you do:
l1.remove(1)
l1.append(4)
Then how could the dict still work, as the dict would then become:
{[3,4]: 'foo', [3,4]: 'bar'}
Hopefully, you got the tuple type which is non-mutable and hashable:
d = {(3,1): 'foo', (3,4): 'bar}
So you might simply want to not .split() it. You might want to strip() it (which will remove any leading and trailing whitespace character).
So here you go:
del_son = raw_input("what father/son pair do you want to delete?(type first name of son").strip()
if del_son in fathers.keys():
del fathers[del_son]
my two cents: to make it slightly more explicit, you'd better use del_son in fathers.keys(). It's actually the same as what you wrote, simply for readability it's easier, as del_son in fathers is misleading (which one could read as: "is the coder removing a son from the list of fathers? WTH?", whereas adding the .keys() will read "Ah! The coder is removing a father from a son→father mapping! Makes sense!")
HTH
A dictionary cannot have a key of type list because the keys MUST be immutable. You can, however, use another immutable type (like a tuple), but that's peripheral to the larger issue. The problem is that you split the name of the son when there is no need to.
del_son = raw_input("what father/son pair do you want to delete?(type first name of son").split(" ")
This is always going to return a list. When you type:
del fathers[del_son]
it indexes the fathers dict with a key of a list, which is not allowed. Simply remove the .split(" ") after the raw_input and it should work.
For the del_son input, you're assigning the result of raw_input("...").split(" "). That split() returns a list. So del_son becomes a list, which can't be used for dictionary keys.
What you want, in this case, is just the first result of the split, so raw_input("...").split(" ")[0]:
del_son = raw_input("what father/son pair ...").split(" ")[0]
That's the first item in the returned list (index 0). Then del_son is just the first element, and a string.
If that raw_input...split...[0] seems too long, you can leave that line as how you have it now and change the next bit to:
if del_son[0] in fathers:
del fathers[del_son[0]]
You are confusing yourself with regard to your index, thereby confusing your dict into a list, since it is now mutable. Looking for a list in a dict, which a list cannot be hashed!
Related
I am trying to make a dictionary of sorts using tuples. The idea is to store a word along with its description in a tuple. The tuple then goes into a list. After that, I'm supposed to be able to look up the meaning of a word in the dictionary by typing the word I want a description of.
My problem is to extract only the description part of the tuple from the list and print only that based on what word the user wants to look up. I do have a function that seems to work for making the tuples and storing them in the list but I think that function also is wrong.
This is as far as I have been able to come:
def tuples():
dictionary = []
while True:
print("\n--- Menu for dictionary ---\n Choose 1 to insert a word\n Choose 2 to lookup a word\n Choose 3 to quit\n")
answer = input("Write your answer here: ")
if answer == "1":
insert(dictionary)
elif answer == "2":
lookup(dictionary)
elif answer == "3":
break
else:
print("\nTry again!\n")
def insert(dictionary):
word = input("What word would you like to add: ")
des = input("Type a description of that word: ")
info = (word, des)
dictionary.append(info)
def lookup(dictionary):
word = input("What word do you want to lookup: ")
place = dictionary.index(word)
print("\nDescription of", word,":", dictionary[place], "\n")
Similar to the other answer, this example loops through the list of tuples examining the word part of the tuple to get to the description part. It differs in a number of respects, but the most important difference is that it uses tuple unpacking versus subscripting to get the contents of the tuple. To illustrate the key concepts I left out the user input part.
Note: If the list of tuples was long enough, you would want to consider sorting it and using something like the the bisect standard library to more efficiently search it and update it.
Example:
dictionary = [("cat", "Four legs, scratches."), ("dog", "Four legs, wags."), ("gerbil", "Four legs, kangaroo-like.")]
def find_description(dictionary, search_term):
# Note use of automatic tuple "unpacking"
for word, description in dictionary:
if word == search_term:
print(f"Description of {word}: {description}")
break
else: # no-break
print(f"Could not find {search_term} in dictionary.")
find_description(dictionary, "gerbil")
find_description(dictionary, "hamster")
Output:
Description of gerbil: Four legs, kangaroo-like.
Could not find hamster in dictionary.
I think you can achieve what you are trying to do by modifying your lookup function
to use a generator expression to search the dictionary list for the query. I got your example to work with the following modification to lookup():
def lookup(dictionary):
word = input("What word do you want to lookup: ")
place = next((i for i, v in enumerate(dictionary) if v[0] == word), None)
print("\nDescription of", word,":", dictionary[place][1], "\n")
If you are concerned with runtime I would recommend abstracting out the (word, des) tuple out to a class that could be hashable such that you can use a dictionary as your dictionary, utilizing the faster lookup. This also would solve the issue of duplicate entries.
I am given an assignment when I am supposed to define a function that returns the second element of a tuple if the first element of a tuple matches with the argument of a function.
Specifically, let's say that I have a list of student registration numbers that goes by:
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
And I have defined a function that is supposed to take in the argument of reg_num, such as "S12345", and return the name of the student in this case, "John". If the number does not match at all, I need to print "Not found" as a message. In essence, I understand that I need to sort through the larger tuple, and compare the first element [0] of each smaller tuple, then return the [1] entry of each smaller tuple. Here's what I have in mind:
def get_student_name(reg_num, particulars):
for i in records:
if reg_num == particulars[::1][0]:
return particulars[i][1]
else:
print("Not found")
I know I'm wrong, but I can't tell why. I'm not well acquainted with how to sort through a tuple. Can anyone offer some advice, especially in syntax? Thank you very much!
When you write for i in particulars, in each iteration i is an item of the collection and not an index. As such you cannot do particulars[i] (and there is no need - as you already have the item). In addition, remove the else statement so to not print for every item that doesn't match condition:
def get_student_name(reg_num, particulars):
for i in particulars:
if reg_num == i[0]:
return i[1]
print("Not found")
If you would want to iterate using indices you could do (but less nice):
for i in range(len(particulars)):
if reg_num == particulars[i][0]:
return particulars[i][1]
Another approach, provided to help learn new tricks for manipulating python data structures:
You can turn you tuple of tuples:
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
into a dictionary:
>>> pdict = dict(particulars)
>>> pdict
{'S12345': 'John', 'S23456': 'Max', 'S34567': 'Mary'}
You can look up the value by supplying the key:
>>> r = 'S23456'
>>> dict(pdict)[r]
'Max'
The function:
def get_student_name(reg, s_data):
try:
return dict(s_data)[reg]
except:
return "Not Found"
The use of try ... except will catch errors and just return Not Found in the case where the reg is not in the tuple in the first place. It will also catch of the supplied tuple is not a series of PAIRS, and thus cannot be converted the way you expect.
You can read more about exceptions: the basics and the docs to learn how to respond differently to different types of error.
for loops in python
Gilad Green already answered your question with a way to fix your code and a quick explanation on for loops.
Here are five loops that do more or less the same thing; I invite you to try them out.
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
for t in particulars:
print("{} {}".format(t[0], t[1]))
for i in range(len(particulars)):
print("{}: {} {}".format(i, particulars[i][0], particulars[i][1]))
for i, t in enumerate(particulars):
print("{}: {} {}".format(i, t[0], t[1]))
for reg_value, student_name in particulars:
print("{} {}".format(reg_value, student_name))
for i, (reg_value, student_name) in enumerate(particulars):
print("{}: {} {}".format(i, reg_value, student_name))
Using dictionaries instead of lists
Most importantly, I would like to add that using an unsorted list to store your student records is not the most efficient way.
If you sort the list and maintain it in sorted order, then you can use binary search to search for reg_num much faster than browsing the list one item at a time. Think of this: when you need to look up a word in a dictionary, do you read all words one by one, starting by "aah", "aback", "abaft", "abandon", etc.? No; first, you open the dictionary somewhere in the middle; you compare the words on that page with your word; then you open it again to another page; compare again; every time you do that, the number of candidate pages diminishes greatly, and so you can find your word among 300,000 other words in a very small time.
Instead of using a sorted list with binary search, you could use another data structure, for instance a binary search tree or a hash table.
But, wait! Python already does that very easily!
There is a data structure in python called a dictionary. See the documentation on dictionaries. This structure is perfectly adapted to most situations where you have keys associated to values. Here the key is the reg_number, and the value is the student name.
You can define a dictionary directly:
particulars = {'S12345': 'John', 'S23456': 'Max', 'S34567': 'Mary'}
Or you can convert your list of tuples to a dictionary:
particulars = (("S12345", "John"), ("S23456", "Max"), ("S34567", "Mary"))
particulars_as_dict = dict(particulars)
Then you can check if an reg_number is in the dictionary, with they keyword in; you can return the student name using square brackets or with the method get:
>>> particulars = {'S12345': 'John', 'S23456': 'Max', 'S34567': 'Mary'}
>>> 'S23456' in particulars
True
>>> 'S98765' in particulars
False
>>>
>>> particulars['S23456']
'Max'
>>> particulars.get('S23456')
'Max'
>>> particulars.get('S23456', 'not found')
'Max'
>>>
>>> particulars['S98765']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'S98765'
>>> particulars.get('S98765')
None
>>> particulars.get('S98765', 'not found')
'not found'
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
Problem
I was trying to assign user input as a key in a dictionary. If user input is a key then print out its value, else print invalid key. The problem is the keys and the values will be from a text file. For simplicity I will just use random data for the text. Any help would be appreciated.
file.txt
Dog,bark
Cat,meow
bird,chirp
Code
def main():
file = open("file.txt")
for i in file:
i = i.strip()
animal, sound = i.split(",")
dict = {animal : sound}
keyinput = input("Enter animal to know what it sounds like: ")
if keyinput in dict:
print("The ",keyinput,sound,"s")
else:
print("The animal is not in the list")
On every iteration of the loop, you are redefining the dictionary, instead, add new entries:
d = {}
for i in file:
i = i.strip()
animal, sound = i.split(",")
d[animal] = sound
Then, you can access the dictionary items by key:
keyinput = input("Enter animal to know what it sounds like: ")
if keyinput in d:
print("The {key} {value}s".format(key=keyinput, value=d[keyinput]))
else:
print("The animal is not in the list")
Note that I've also changed the dictionary variable name from dict to d, since dict is a poor variable name choice because it is shadowing the built-in dict.
Also, I've improved the way you construct the reporting string and used a string formatting instead. If you would enter Dog, the output would be The Dog barks.
You can also initialize the dictionary in one line using the dict() constructor:
d = dict(line.strip().split(",") for line in file)
As a side note, to follow the best practices and keep your code portable and reliable, use the with context manager when opening the file - it would take care about closing it properly:
with open("file.txt") as f:
# ...
OP, I've written some verbose explanatory notes in the code and fixed a few issues; I might have overlooked something but check it out.
For one, avoid using dict as a variable name since it shadows Python's bult-in dict method.
Remember that , in most cases, you need to declare variables before a loop to make them accessible after the loop; this applies to your dictionary.
Also, remember to close files after reading/writing unless you use with open(filename) ...
def main():
# declare a new, empty dictionary to hold your animal species and sounds.
# Note that I'm avoiding the use of "dict" as a variable name since it
# shadows/overrides the built-in method
animal_dict = {}
file = open("file.txt")
for i in file:
i = i.strip()
animal, sound = i.split(",")
animal_dict[animal] = sound
# Remember to close your files after reading
file.close()
keyinput = input("Enter animal to know what it sounds like: ")
if keyinput in animal_dict:
# here, keyinput is the string/key and to do a lookup
# in the dictionary, you use brackets.
# animal_dict[keyinput] thus returns the sound
print("The ",keyinput,animal_dict[keyinput],"s")
else:
print("The animal is not in the list")
There were comments on every line I changed something explaining what I changed, but to help readability, I'm putting them here too.
On Line 2 I instantiated a dictionary - you were previously
re-defining a dictionary for each line
On Line 7 I changed your
code to add something to the dictionary instead of just creating a
new one. That's proper dictionary syntax.
On Line 10 I changed "if
keyinput in dict" to "if keyinput in dict.keys()", since you're
checking to see if the animal exists, and the animals in your file
become the keys of the dictionary.
def main():
dict = {} #Create an empty dictionary to add to
file = open("file.txt")
for i in file:
i = i.strip()
animal, sound = i.split(",")
dict[animal] = sound #This is how you add a key/value pair to a dictionary in Python
keyinput = input("Enter animal to know what it sounds like: ")
if keyinput in dict.keys(): #Add .keys() to iterate through dictionary keys
print("The ",keyinput,sound,"s")
else:
print("The animal is not in the list")
First of all you should not name a variable the same as a keyword. Secondly, the way you put the input into the dictionary will overwrite the previous values. You need to create the dictionary and then add the new values.
Third, you output the value sound without getting it from the dictionary
dict as a variable should be named mydict
create mydict = {} before the initial loop
set mydict[animal] = sound in the first loop
mydict['dog'] = 'bark' # This is what happens
print keyinput and mydict[keyinput] if it is in the list.
You can also use mysound = mydict.get(keyinput, "not in dictionary") instead of the if.
Why dict.get(key) instead of dict[key]?
I've been programming for less than four weeks and have run into a problem that I cannot figure out. I'm trying to append a string value to an existing key with an existing string stored in it but if any value already exists in the key I get "str object has no attribute 'append'.
I've tried turning the value to list but this also does not work. I need to use the .append() attribute because update simply replaces the value in clientKey instead of appending to whatever value is already stored. After doing some more research, I understand now that I need to somehow split the value stored in clientKey.
Any help would be greatly appreciated.
data = {}
while True:
clientKey = input().upper()
refDate = strftime("%Y%m%d%H%M%S", gmtime())
refDate = refDate[2 : ]
ref = clientKey + refDate
if clientKey not in data:
data[clientKey] = ref
elif ref in data[clientKey]:
print("That invoice already exists")
else:
data[clientKey].append(ref)
break
You can't .append() to a string because a string is not mutable. If you want your dictionary value to be able to contain multiple items, it should be a container type such as a list. The easiest way to do this is just to add the single item as a list in the first place.
if clientKey not in data:
data[clientKey] = [ref] # single-item list
Now you can data[clientkey].append() all day long.
A simpler approach for this problem is to use collections.defaultdict. This automatically creates the item when it's not there, making your code much simpler.
from collections import defaultdict
data = defaultdict(list)
# ... same as before up to your if
if clientkey in data and ref in data[clientkey]:
print("That invoice already exists")
else:
data[clientKey].append(ref)
You started with a string value, and you cannot call .append() on a string. Start with a list value instead:
if clientKey not in data:
data[clientKey] = [ref]
Now data[clientKey] references a list object with one string in it. List objects do have an append() method.
If you want to keep appending to the string you can use data[clientKey]+= ref
I'm fairly new to Python but am ok with programming (although haven't done any for about 5 years).
I've searched but can't find anything to answer my problem:
I have a number of lists, each with values in them, I'm trying to create a generic function that takes 2 values that searches a list, the 2 values are obviously the list name, and the string to search for in that list:
list0 = ["name","date","cat","dog"]
list1 = ["house","chair","table"]
list2 = ["tv","dvd","computer","mouse"]
usersearchlist = raw_input("Enter list name: ")
usersearchitem = raw_input("Enter item to search for: ")
def searchmemory(usersearchlist,usersearchitem):
return usersearchlist.index(usersearchitem)
I then call the function:
print "I found", searchmemory(usersearchlist,usersearchitem)
I'm getting the "ValueError: substring not found" because the function call is taking the literal string passed into the function and not referencing the value contained inside of it.
Hope that makes sense, am I doing something totally wrong?
try
lists = {"list0" : ["name","date","cat","dog"],
"list1" : ["house","chair","table"],
"list2" : ["tv","dvd","computer","mouse"]}
usersearchlist = raw_input("Enter list name: ")
usersearchitem = raw_input("Enter item to search for: ")
def searchmemory(usersearchlist, usersearchitem):
if (usersearchlist in lists and usersearchitem in lists[usersearchlist]):
return lists[usersearchlist].index(usersearchitem)
else:
return -1
This stores all the lists in a dictionary and checks if the value exists first so you shouldn't get a ValueError
I would prefer to put the lists in a dictionary similar to #SimplyKiwi 's answer.
Alternatively, you can also achieve it with globals():
print "I found", searchmemory(globals().get(usersearchlist, []), usersearchitem)
Be warned though in this case, you are implicitly giving the user access to all the global variables. In most scenario, this is not what you would want the users to be able to do.