Learn Python The Hard Way - Exercise 39 - python

On Exercise 39 of Learn Python The Hard Way, lines 37 to 39 look like this:
print "-"*10
for state, abbrev in states.items():
print "%s has the city %s" % (state, abbrev)
I thought I understood this. I thought Python was taking the KEY:VALUE from "states" and assigning the KEY to "state" and the VALUE to "abbrev".
However, I found something strange happened when I entered the following code:
print "-"*10
for test in states.items():
print "%s has the city %s" % (test)
It produces the same output as the original code.
But, it only works if you put the %s into the print statement twice.
Can someone explain what is happening with "test"?
What exactly is "test"? Is it a Tuple?
It seems to contain both the KEY and the VALUE from states.items().
I have looked through some of the other questions on Exercise 39 here and I haven't found the same query.
The code is listed below (for Python 2.7)
# create a mapping of state to abbreviation
states = {
'Oregan': 'OR',
'Florida': 'FL',
'California': 'CA',
'New York' : 'NY',
'Michigan' : 'MI'
}
print "-"*10
for state, abbrev in states.items():
print "%s has the city %s" % (state, abbrev)
print "-"*10
for test in states.items():
print "%s has the city %s" % (test)

states is a dictionary, so when you called for test in states.items() it assigns each item of the dictionary (a tuple) to test.
Then you are just iterating over the items and printing their keys and values as you would with for state, abbrev in states.items():
>>> for state in states.items():
print (state) # print all the tuples
('California', 'CA')
('Oregan', 'OR')
('Florida', 'FL')
('Michigan', 'MI')
('New York', 'NY')
All the details are available online, for instance in PEP 234 -- Iterators under Dictionary Iterators:
Dictionaries implement a tp_iter slot that returns an efficient iterator that iterates over the keys of the dictionary. [...] This means that we can write
for k in dict: ...
which is equivalent to, but much faster than
for k in dict.keys(): ...
as long as the restriction on modifications to the dictionary (either by the loop or by another thread) are not violated.
Add methods to dictionaries that return different kinds of iterators explicitly:
for key in dict.iterkeys(): ...
for value in dict.itervalues(): ...
for key, value in dict.iteritems(): ...
This means that for x in dict is shorthand for for x in
dict.iterkeys().

This "missing link" between your first and second code snippet explains why they are equivalent:
print "-"*10
for test in states.items():
state, abbrev = test
print "%s has the city %s" % (state, abbrev)

Related

Can i put anything in for x

I am learning how to code in python so I'm wondering if we can put anything in for x (key in this instance):
for key in prices:
print key
print "price: %s" % prices[key]
print "stock: %s" % stock[key]
Yes, you can put any name there that you wish -- just as long as it's a valid variable name in Python. key is going to be the variable name for each item inside the for block.

Appending list values to a dictionary key

I am trying to create a python script and I am stuck with the dictionaries. I have read through some of the other forums but can't seem to get anywhere. I am a very new python programmer so please be gentle.
What I want to do:
1) set up a dictionary like this: {'Name':'userid','jobid:jobid','walltime:walltime,'nodes:nds'}
2) iterate through a list of entries created from and external function call and extract information to populate the dictionary
3) Problem: I cannot figure out how to append entries to the appropriate keys
For example, I want this:
{‘Name’:’jose’,’jobid’:’001,002,003,005’,’walltime:32:00,240:00,04:00,07:00’,’nodes’:32,32,500’}
Notice for one userid, I have multiple jobids, walltimes and nodes.
(len(jobids)==len(walltimes)==len(nodes) for any one userid but can vary across userids)
I am able to get the script to find the first value for each username, but it never appends. How can I get this to append?
Here is what I have tried
from collections import defaultdict
pdict = defaultdict(list)
start the loop:
# get new values – add these to the dictionary keyed
# on username (create a new entry or append to existing entry)
…
(jobid,userid,jobname, sessid, nds, tsk, walltime,rest)= m.groups()
...
if userid in pdict:
print "DEBUG: %s is currently in the dictionary -- appending entries" %(userid)
pdict[userid][‘jobid’] = pdict[userid][jobid].append(jobid)  I
# repeat for nodes, walltime, etc
if not userid in pdict:
print "DEBUG: %s is not in the dictionary creating entry" %(userid)
pdict[userid] = {} # define a dictionary within a dictionary with key off userid
pdict[userid]['jobid'] = jobid
pdict[userid]['jobname'] = jobname
pdict[userid]['nodes'] = nds
pdict[userid]['walltime'] = walltime
I know this is wrong but can’t figure out how to get the append to work. I have tried many of the suggestions offered on this site. I need to append (to the dictionary) the most recent values from the loop keyed to userid
Here is an example of the ouput – it does not append multiple entries for each userid but rather takes only the first value for each userid
userid jmreill contains data: {'nodes': '1', 'jobname':
'A10012a_ReMig_Q', 'walltime': '230:0', 'jobid': '1365582'}
userid igorysh contains data: {'nodes': '20', 'jobname':
'emvii_Beam_fwi6', 'walltime': '06:50', 'jobid': '1398100'}
Any suggestions? This should be easy but I can’t figure it out!
from collections import defaultdict
pdict = defaultdict(dict)
start the loop:
# get new values – add these to the dictionary keyed
# on username (create a new entry or append to existing entry)
…
(jobid,userid,jobname, sessid, nds, tsk, walltime,rest)= m.groups()
...
if userid in pdict:
print "DEBUG: %s is currently in the dictionary -- appending entries" %(userid)
pdict[userid][jobid].append(jobid)
# repeat for nodes, walltime, etc
if userid not in pdict:
print "DEBUG: %s is not in the dictionary creating entry" %(userid)
pdict[userid]['jobid'] = [jobid]
pdict[userid]['jobname'] = jobname
pdict[userid]['nodes'] = nds
pdict[userid]['walltime'] = walltime
The value corresponding to key 'jobid' should be a list of strings rather than a string. If you create your dictionary this way, you can append new jobid's to the list simply by:
pdict[userid]['jobid'].append(jobid)
I cant remember the explanation why to use the lambda expression in the following code, but you have to define a defaultdict of a defaultdict:
pdict = defaultdict(lambda: defaultdict(list))
pdict[userid][‘jobid’].append('1234')
will work.
The append() method does not return the list...it is modified in place. Also, you need to initialize your elements as lists (using square brackets):
if userid in pdict:
print "DEBUG: %s is currently in the dictionary -- appending entries" %(userid)
pdict[userid][jobid].append(jobid) ## just call append here
# repeat for nodes, walltime, etc
if not userid in pdict:
print "DEBUG: %s is not in the dictionary creating entry" %(userid)
pdict[userid] = {} # define a dictionary within a dictionary with key off userid
pdict[userid]['jobid'] = [jobid,] ## initialize with lists
pdict[userid]['jobname'] = [jobname,]
pdict[userid]['nodes'] = [nds,]
pdict[userid]['walltime'] = [walltime,]
append doesn't return a value, it modifies the list in place, and you forgot to quote 'jobid' on the right of equals. So you should replace pdict[userid][‘jobid’] = pdict[userid][jobid].append(jobid) with pdict[userid]['jobid'].append(jobid). Also take into account comment from #Jasper.
You are looking for a dict of dicts? AutoVivification is the perfect solution. Implement the perl’s autovivification feature in Python.
class AutoVivification(dict):
"""Implementation of perl's autovivification feature."""
def __getitem__(self, item):
try:
return dict.__getitem__(self, item)
except KeyError:
value = self[item] = type(self)()
return value​
This makes everything easier. Note that the value of pdict[userid]['jobid'] should be a list [jobid] instead of a variable jobid as you have multiple jobid.
pdict = AutoVivification()
if userid in pdict:
pdict[userid]['jobid'].append(jobid)
else:
pdict[userid]['jobid'] = [jobid] # a list
Refer to What is the best way to implement nested dictionaries in Python?.

How to throw exception when re.search item is not present in list or dict

I'm reading a file and putting contents into dictionary. I'm writing a method where I search for key and return its value. How do I throw exception if my key is not present in dictionary. For example below is the code I'm testing but I get output of re.search as None for non-match items.
Can I use has_key() method?
mylist = {'fruit':'apple','vegi':'carrot'}
for key,value in mylist.items():
found = re.search('vegi',key)
if found is None:
print("Not found")
else:
print("Found")
Found
Not found
Python trends towards the "Easier to Ask Forgiveness than Permission" model versus "Look Before You Leap". So in your code, don't search for the key before trying to pull it's value, just pull for it's value and handle the fallout as needed (and where needed).
*Assuming you're asking how to find one key, and return it's value.
EAFP approach:
def some_func(key)
my_dict = {'fruit':'apple', 'vegi':'carrot'}
return my_dict[key] # Raises KeyError if key is not in my_dict
If a LBYP is what you have to do, try this:
def some_func(key):
my_dict = {'fruit':'apple', 'vegi':'carrot'}
if not key in my_dict:
raise SomeException('my useful exceptions message')
else:
return my_dict[key]
The biggest problem with the LBYP approach is that it introduces a race condition; the 'key' may or may not exist between checking for it, then returning it's value (which is only possible when doing current work).
You can simply use 'in'.
mylist = {'fruit':'apple','vegi':'carrot'}
test = ['fruit', 'vegi', 'veg']
for value in test:
if value in mylist:
print(value + ' is in the dict, its value : ' + mylist[value])
else:
raise Exception(value + ' not in dict.')
# Console
# fruit is in the dict, its value: apple
# vegi is in the dict, its value: carrot
# Exception: veg is not in dict
#JRazor gave you several ways of using list comprehension, lambda and filter for doing what you call a "has_key() method" (I get SyntaxErrors when I copy/paste them to python 2.7 interpreter though?)
Here's the literal answer to your question: "How do I throw exception if my key is not present in dictionary?"
What many languages refer to as throw (an exception), python calls raise (an exception).
More info on that here.
In your case, you could add a custom exception like so:
mylist = {'fruit':'apple','vegi':'carrot'} # mylist is a dictionary. Just sayin'
if "key" not in mylist:
raise Exception("Key not found")
else:
print "Key found"

Python keyword output interpretation

I'm going through the Python 2.7 tutorial, and I was looking at the output of the following statement:
def cheeseshop(kind, *arguments, **keywords):
print "-- Do you have any", kind, "?"
print "-- I'm sorry, we're all out of", kind
for arg in arguments:
print arg
print "-" * 40
keys = sorted(keywords.keys())
for kw in keys:
print kw, ":", keywords[kw]
So, if I call the program as such:
cheeseshop("Cheddar", "No.", "Seriously?",
Shopkeeper="Michael Palin",
Client="John Cleese")
It outputs:
Do you have any Cheddar?
I'm sorry, we're all out of Cheddar
No.
Seriously?
--------------------------------------
Client: John Cleese
Shopkeeper: Michael Palin
This is correct.
If I change that print statement to print keywords, I get the following representation:
{'Shopkeeper': 'Ryan Lambert', 'Client': 'John Cleese'}
I'm a bit confused on how printing keywords[kw] just comes back with a name, and keywords does not.
In Python, you can pass optional keyword parameters by putting a ** in front of the function parameter's list.
So the keywords variable is actually a dictionary type. Thus, if you do:
print keywords
you get back (reformatted to make the mapping more obvious)
{
'Shopkeeper': 'Ryan Lambert',
'Client': 'John Cleese'
}
which is a dictionary. And if you do:
print keywords[kw]
you get back the value of the dictionary associated with the key kw. So if kw was 'Shopkeeper', then keywords[kw] becomes 'Ryan Lambert', and if kw was 'Client', then keywords[kw] becomes 'John Cleese'
keywords is stored as a dictionary. ( See this for more)
If you print the dictionary itself it is going to output the complete set of pairs it contains (key,value).
On your program:
keys are: 'Shopkeeper' and 'Client'
values are respectively: 'Ryan Lambert' and 'John Cleese'
One way to access the values is to "search" for it with its key: dict[key]
So when you wrote: "keywords[kw]" you are actually passing a key and python is going to give you the corresponding value.
You can think it as similar as accessing an array value:
a = ['a', 'b', 'c']
if you:
print a #output: ['a', 'b', 'c']
print a[0] # outputs: 'a'
just unlike arrays the data is not stored "neatly" together in memory, but using hashing
Hope it helped, Cheers
When you call the function with
cheeseshop("Cheddar", "No.", "Seriously?",
Shopkeeper="Michael Palin", Client="John Cleese")
the keywords parameter takes on the value {'Shopkeeper': 'Ryan Lambert', 'Client': 'John Cleese'}, i.e., it's a dictionary.
This is equivalent to (and much easier to read than) calling the function as
cheeseshop("Cheddar", *["No.", "Seriously?"],
**{"Shopkeeper":"Michael Palin", "Client":"John Cleese"})
That is, the values in the first function call are automatically wrapped inside the *arguments and **keywords parameters -- that's what those * and ** are for.
Now, when you do this:
keys = sorted(keywords.keys())
for kw in keys:
print kw, ":", keywords[kw]
keyword.keys() is ['Shopkeeper', 'Client'], i.e. the "keys" in the dictionary. Next, you sort those keys and for each key, you print the respective entry in the dictionary, e.g., "John Cleese" for "Client".

Printing from two dictionaries

states = {state, abbr}
cities = {abbr, capital}
I'd like to print out state, abbr, capital or some other order of the 3 values.
I tried inverting the key, value pair in states with:
inv_states = {state: abbr for abbr, state in states.items()}
for abbr, capital in sorted(inv_states.items()):
print(abbr, ':', capital)
Now I've got:
states = {abbr, state}
cities = {abbr, capital}
And then trying to create a new dict with (I don't fully understand the below code):
newDict = defaultdict(dict)
for abbr in (inv_states, cities):
for elem in abbr:
newDict[elem['index']].update(elem)
fullDict = newDict.values()
print(fullDict)
But I'm getting a string indices must be intergers error.
Little help please. Or am I completely on the wrong path? Thanks.
Assuming your data is in dictionaries, (which it is not in your example code). It should be pretty straightforward to pring out the data you are looking for
for state, abbr in states.items():
print('{0}, {1}, {2}'.format(state, abbr, cities[abbr]))

Categories