Python program using `defaultdicts` doesn't work as expected - python

Expected result:
main_dict = {
'a':
{ 0: 'Letter a',
1: 'Letter a',
2: 'Letter a',},
'b':
{ 0: 'Letter b',
1: 'Letter b',
2: 'Letter b',},
'c':
{ 0: 'Letter c',
1: 'Letter c',
2: 'Letter c',}
}
My program, version 1; the expected results is the output.
# my_program.py
def fill_dict(a_dict, a_key):
if not a_dict.has_key(a_key):
a_dict[a_key] = {}
for i in xrange(3):
a_dict[a_key][i] = 'Letter {}'.format(a_key)
def main():
main_dict = {}
a_list = ['a', 'b', 'c']
for item in a_list:
fill_dict(main_dict, item)
if __name__ == '__main__':
main()
Refactored program using defaultdicts; result is main_dict = {}.
# defaultdict_test.py
import collections
def fill_dict(a_dict, a_key):
a_dict = collections.defaultdict(dict)
for i in xrange(3):
a_dict[a_key][i] = 'Letter {}'.format(a_key)
def main():
main_dict = {}
a_list = ['a', 'b', 'c']
for item in a_list:
fill_dict(main_dict, item)
if __name__ == '__main__':
main()
Any pointers on what I'm doing wrong? Thanks.

You are passing main_dict into fill_dict, then assigning a new defaultdict to the local variable a_dict. You never pass that value back out.
In your program that works, you don't reassign the local, so when you call methods on a_dict, you are modifying the value passed in, which is the main_dict value from main.
This distinction, between reassigning, and mutating with methods, is subtle but important. This article has more on names, values, and their interactions: Facts and myths about Python names and values.

Pointers:
Use in vs has_key in your first version. The method has_key has been depricated and removed in Python 3
Remove a_dict = collections.defaultdict(dict) from fill_dict
Change main_dict = {} to main_dict = collections.defaultdict(dict) in main
Done!
import collections
def fill_dict(a_dict, a_key):
for i in xrange(3):
a_dict[a_key][i] = 'Letter {}'.format(a_key)
def main():
main_dict = collections.defaultdict(dict)
a_list = ['a', 'b', 'c']
for item in a_list:
fill_dict(main_dict, item)
Final pointer: Get to know list, set and dict comprehensions. You can do this data structure in a single line:
>>> {c:{i: 'Letter {}'.format(c) for i in range(3)} for c in 'abc'}
{'a': {0: 'Letter a', 1: 'Letter a', 2: 'Letter a'}, 'c': {0: 'Letter c', 1: 'Letter c', 2: 'Letter c'}, 'b': {0: 'Letter b', 1: 'Letter b', 2: 'Letter b'}}
If you look at it -- it is almost a version of what you have as your desired result if you format it that way:
dict_you_want={
c:
{ i: 'Letter {}'.format(c)
for i in range(3) } # ,
for c in 'abc'
}
which will execute just as I have it there...

Related

Dictionary/recursive/ counting parts (exercise[LOGIC])

problem: create a recursive function that given an input key, would return the amount of basic components to build the given input key.
EX 1) input = "Engine"
output = Engine ==> metal: 3, rubber: 2
EX 2) input = "metal"
output = metal ==> metal: 1
EX 3) input = "piston"
output = piston ==> metal: 1, rubber: 1
car= {
"Engine" : ["pistons", "timing belt", "metal" ,"metal"],
"Pistons" : ["Metal", "rubber"],
"timing belt" : ["rubber"],
"metal" : [],
"rubber" : []
}
my code has different variable names and key name, but it's the same idea
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D','E','F'],
'D': [],
'E': ['B','D'],
'F': []
}
#above here its user input
counter_dictio = {
'A': [],
'B': [],
'C': [],
'D': [],
'E': [],
'F': []
}
def desamble(key, dictionary):
#check if array is empty
#ccounter +=1
if (len(dictionary[key])) == 0:
counter_dictio[key].append(key)
#if array is populated
#enter to this array
#desample(i, dictionary)
else:
for i in dictionary[key]:
desamble(i, dictionary)
key = "A"
desamble(key, parts)
One way to go is:
from collections import Counter
car= {
"engine": ["pistons", "timing belt", "metal", "metal"],
"pistons": ["metal", "rubber"],
"timing belt": ["rubber"],
"metal": [],
"rubber": []
}
def ingredients(key, dct):
if dct[key] == []:
yield key
else:
for sub_part in dct[key]:
yield from ingredients(sub_part, dct)
print(*ingredients('engine', car)) # metal rubber rubber metal metal
print(Counter(ingredients('engine', car))) # Counter({'metal': 3, 'rubber': 2})
ingredients makes a generator of ingredients, so you can use Counter to count them.
Here is code that will group by your components
from collections import defaultdict
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D', 'E', 'F'],
'D': [],
'E': ['B', 'D'],
'F': []
}
result = defaultdict(dict)
for k, v in parts.items():
row = result[k] # used to create empty dict on initial empty list
for item in v:
if row.get(item) is None:
row[item] = 1
else:
row[item] += 1
This will result in following dict
{'A': {'B': 2, 'C': 1}, 'B': {}, 'C': {'D': 1, 'E': 1, 'F': 1}, 'D': {}, 'E': {'B': 1, 'D': 1}, 'F': {}}
another solution without using recursively, and for a predetermined list would be:
#==== user input====
key = "E"
parts = {
'A': ['B', 'B', 'C'],
'B': [],
'C': ['D','E','F'],
'D': [],
'E': ['B','D'],
'F': []
}
#====end user input=====
#Function for the predetermined dictionary
def desamble(key, dictionary):
if key == "B" or key == "D" or key == "F":
print(key + "==> " + key + ": 1")
elif key == "E":
print(key + "==> " + "B: 1, D: 1" )
elif key == "C":
print(key + "==> " + "B: 1, D: 2, F: 1" )
elif key == "A":
print(key + "==> " + "B: 3, D: 2, F: 1" )
else:
print("Key " + key + " is not defined in dictionary")
#====end====
desamble(key, parts)
Another recursive way to solve this problem, adding a solution for the problem of circularity, meaning that in case that the parts call to eachother.
EX)
dictionary = {
"A": "B",
"B": "A"
}
from typing iport Counter
def func(key, dictionary, current=None):
if not current:
current = set() # could also be a list
if key in current:
raise ValueError # or whichever
if not dictionary.get(key):
return [key]
ret = []
for subkey in dictionary[key]:
ret.extend(func(subkey, dictionary, current.union({key})))
return ret
Print(Counter(func("A"), parts))
#by officerthegeeks

Python: How do I iterate over more than 2 dictionaries?

Not really sure what I'm doing wrong here. I thought the zip method would work to check if a value is in multiple lists? What I'd want it to do is check to see if that value is in any of those dictionaries, and if so, to print its key, but if not, then print only one string of ('Not in Any Dictionary'). This method prints 40 of them for some reason with the real dictionaries.
MLB_Teams = {1: 'New York Yankees', 2: 'Pittsburgh Pirates'}
NBA_Teams = {1: 'Houston Rockets', 2: 'Brooklyn Nets'}
NFL_Teams = {1: 'Philadelphia Eagles', 2: 'Detroit Lions'}
for (key,value), (key,value), (key, value) in zip(MLB_Teams.items(), NBA_Teams.items(), NFL_Teams.items()):
reply = 'Houston Rockets'
if reply == value:
print(key)
else:
print('Not In Any Dictionary')
I think you can do it in a very simple way:
MLB_Teams = {1: 'New York Yankees', 2: 'Pittsburgh Pirates'}
NBA_Teams = {1: 'Houston Rockets', 2: 'Brooklyn Nets'}
NFL_Teams = {1: 'Philadelphia Eagles', 2: 'Detroit Lions'}
v = 'Philadelphia Eagles'
def find_in_dict(val, d):
for k, v in d.items():
if v == val:
print(k)
return True
for dd in (MLB_Teams, NBA_Teams, NFL_Teams):
if find_in_dict(v, dd):
break
else:
print('Not In Any Dictionary')
The issue lies in how you've reused the variable names for key and value. Add a print statement to see the effect.
for (key,value), (key,value), (key, value) in zip(MLB_Teams.items(), NBA_Teams.items(), NFL_Teams.items()):
print(value) #added
reply = 'Houston Rockets'
if reply == value:
print(key)
else:
print('Not In Any Dictionary')
#output
Philadelphia Eagles
Not In Any Dictionary
Detroit Lions
Not In Any Dictionary
The variables key and value get reassigned to the last entry in the tuple.
You can use the zip just fine if you handle unpacking later.
MLB_Teams = {1: 'New York Yankees', 2: 'Pittsburgh Pirates'}
NBA_Teams = {1: 'Houston Rockets', 2: 'Brooklyn Nets'}
NFL_Teams = {1: 'Philadelphia Eagles', 2: 'Detroit Lions'}
reply = 'Houston Rockets'
for tups in zip(MLB_Teams.items(), NBA_Teams.items(), NFL_Teams.items()):
if any(reply == val for key,val in tups):
print(tups[0][0]) #key
else:
print('Not In Any Dictionary')
#output
1
Not In Any Dictionary

How to make python go through specific part of for loop go first?

Say we got a list (it has more lists within this list, but ive included one just for demonstration purposes):
list = [
['X', ['House A', 'Location 4', 'Right'],
['House D', 'Location 3', 'Left'],
['House C', 'Location 2', 'Right']]
And I made a function like this:
def example(lists):
for element in lists:
if element[0] == 'House A':
if element[1] == 'Location 4':
if element[2] == 'Right':
direction = 0
house1(loc4, direction)
elif element[2] == 'Left':
direction = 180
house1(loc4, direction)
if element[0] == 'X':
graffiti()
How can I make it so that if everything is true after the for-loop (I may have not done the for loop correctly in this example but it is correct in the proper version of my code), it draws the graffiti(). (Doing turtle graphics)
I didn't test this code:
direction_map = {'Right': 0, 'Left': 180}
def example(lists):
for element in lists:
if element[0] == 'House A' and element[1] == 'Location 4':
house1(loc4, direction_map[element[2]])
if list(element for element in lists if (element[0] == 'House A'
and element[1] == 'Location 4')) and (element[2] in direction_map)):
....
if element[0] == 'X':
graffiti()
but from what I understood from what you posted maybe any method is what you are looking for.

Why is are the dicts in this list of dicts empty?

As the title implies; I'm unsure as to why the dictionaries in this list of dictionaries are empty. I print the dictionaries out before I append them to the list and they all have 4 keys/values.
Please ignore the 'scrappiness' of the code- I always go through a process of writing it out basically and then refining!
Code:
import ntpath, sys, tkFileDialog, Tkinter
import xml.etree.ElementTree as ET
class Comparison:
def __init__(self, file):
self.file = ET.parse(file)
self.filename = self.get_file_name(file)
self.root = self.file.getroot()
self.file_length = len(self.root)
self.data_dict = dict()
self.master_list = list()
self.parse_xml(self.root)
print self.master_list
def get_file_name(self, file):
filename_list = list()
for char in ntpath.basename(str(file)):
filename_list.append(char)
if ''.join(filename_list[-4:]) == '.xml':
return ''.join(filename_list)
def parse_xml(self, tree):
for child in tree:
if tree == self.root:
self.step_number = child.attrib['id']
self.data_dict['Step'] = self.step_number
if len(child.tag) > 0:
self.data_dict['Tag'] = child.tag
else:
self.data_dict['Tag'] = ""
if len(child.attrib) > 0:
self.data_dict['Attrib'] = child.attrib
else:
self.data_dict['Attrib'] = ""
if child.text is not None:
self.data_dict['Text'] = child.text
else:
self.data_dict['Text'] = ""
print self.data_dict
print "Step: "+str(self.data_dict['Step'])
try:
print "Tag: "+str(self.data_dict['Tag'])
except:
pass
try:
for key,value in self.data_dict['Attrib'].iteritems():
print "Attrib: "+str(key)
print "Attrib Value: "+str(value)
except:
pass
try:
if len(str(self.data_dict['Text'])) > 0:
print "Text Length: "+str(len(str(self.data_dict['Text'])))
print "Text: "+str(self.data_dict['Text'])
except:
pass
print ""
print len(self.data_dict)
self.master_list.append(self.data_dict)
print self.data_dict
self.data_dict.clear()
if len(child) > 0:
self.parse_xml(child)
if __name__ == "__main__":
root = Tkinter.Tk()
root.iconify()
if sys.argv[1] != "":
file_a = Comparison(sys.argv[1])
else:
file_a = Comparison(tkFileDialog.askopenfilename())
I presume you mean this:
print len(self.data_dict)
self.master_list.append(self.data_dict)
print self.data_dict
self.data_dict.clear()
The dict is empty because you clear it. Everything is a reference in Python.
>>> d = {k:v for k,v in zip(range(5),'abcde')}
>>> id(d)
140199719344392
>>> some_list = []
>>> some_list.append(d)
>>> some_list
[{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}]
>>> id(some_list[0])
140199719344392
>>> d.clear()
>>> some_list
[{}]
>>>
If you want a copy to be appended, then you need to explicitely copy it. If a shallow copy will do, then simply use your_dict.copy():
>>> d = {k:v for k,v in zip(range(5),'abcde')}
>>> d
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}
>>> id(d)
140199686153480
>>> some_list = []
>>> some_list.append(d.copy())
>>> id(some_list[0])
140199719344392
>>> d.clear()
>>> some_list
[{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}]
>>>

Modifying nested dictionaries

Given this two dicts:
empty = {'151': {'1': 'empty', '0': 'empty', '2': '2.30'}}
full = {'151': {'1': 3.4, '0': 3.6, '2': 2}}
Firstly, I want to check if empty.keys() == full.keys() if it holds, I want to replace the empty values with corresponding value from full dictionary. It ought to result in:
not_empty = {'151': {'1': '3.4', '0': '3.6', '2': '2.30'}}
My solution so far: I thought I would identify all the keys with empty values using regex, but for whatever reason my code so far, produces an empty dict {}.
import re
find_empty = re.findall("'(\d)':\s'empty'", str(empty))[0]
if empty.keys() == full.keys():
k = empty.values()[0].keys()
v = empty.values()[0].values()
print {k:v for k,v in empty.values()[0].iteritems()\
if empty.values()[0][find_empty] != 'empty'}
I hoped it can output {'151': {'2': '2.30'}} for a good starting point. Anyway, I guess there exists more clean solution then regex for this task so any hints are welcomed!
Regex is not the right tool for this job. I would suggest a recursive approach like the following.
empty = {'151': {'1': 'empty', '0': 'empty', '2': '2.30'}}
full = {'151': {'1': 3.4, '0': 3.6, '2': 2}}
def repl(a, b):
clean = {}
for k, v in a.items():
# This is the case where we want to replace what we have in b if we have something. Just in case, use the dict.get method and provide a default.
if v == 'empty':
clean[k] = b.get(k, 'Not there')
# If the value is another dict, then call this function with the value, and put the return as the value for our current key
elif isinstance(v, dict):
v_clean = repl(v, b.get(k, {}))
clean[k] = v_clean
# The value isn't equal to 'empty', and it isn't another dict, so just keep the current value.
else:
clean[k] = v
# Finally, return the cleaned up dictionary.
return clean
print repl(empty, full)
OUTPUT
{'151': {'1': 3.4, '0': 3.6, '2': '2.30'}}
EDIT I am not sure if this takes care of all of your cases, but it probably worth a look anyway.
empty = {'151': {'1': 'empty', '0': 'empty', '2': '2.30', '8': ['empty', 'empty', 5, {"foo2": "bar2", "1": "empty"}]}}
full = {'151': {'1': 3.4, '0': 3.6, '2': 2, '8': ['foo', 'bar', 'baz', {"foo3": "bar3", "1": "2"}]}}
def repl(a, b):
if isinstance(a, dict) and isinstance(b, dict):
clean = {}
for k, v in a.items():
# This is the case where we want to replace what we have in b if we have something. Just in case, use the dict.get method and provide a default.
if v == 'empty':
clean[k] = b.get(k, 'Not there')
# If the value is another dict, then call this function with the value, and put the return as the value for our current key
elif isinstance(v, dict):
v_clean = repl(v, b.get(k, {}))
clean[k] = v_clean
# The value isn't equal to 'empty', and it isn't another dict, so just keep the current value.
elif isinstance(v, list):
v_clean = repl(v, b.get(k, []))
clean[k] = v_clean
else:
clean[k] = v
# Finally, return the cleaned up dictionary.
elif isinstance(a, list) and isinstance(b, list):
clean = []
for item_a, item_b in zip(a, b):
if item_a == 'empty':
clean.append(item_b)
elif isinstance(item_a, dict):
clean_a = repl(item_a, item_b)
clean.append(clean_a)
else:
clean.append(item_a)
return clean
print repl(empty, full)
OUTPUT
{'151': {'1': 3.4, '0': 3.6, '2': '2.30', '8': ['foo', 'bar', 5, {'1': '2', 'foo2': 'bar2'}]}}

Categories

Resources