July 28th, 2021
Today: tuples, dict.items() output pattern, lambda, map, one-liners
Say for our building we have a "temps" dict with a key for each room - 'room1', 'room2', etc. The value for each room is a nested dict of the sensors in the at room 't1', 't2, with the value being the temperature.
>>> d = {'room1': {'t1': 100, 't2': 102}}
'room1'
Q: What is an expression to access the nested 'room1'
dict?
A: This is what square brackets do, so it's dict[key], or in this case: d['room1']
't1'
in 'room1'
Q: What is an expression to get the temperature of sensor 't1'
in 'room1'
?
A: The square brackets work left-to-right. So the expression d['room1']
points to the inner, nested dict. You can add another pair of brackets on the right to access inside the nested dict"
>>> d = {'room1': {'t1': 100, 't2': 102}} >>> >>> d['room1'] # access nested {'t1': 100, 't2': 102} >>> >>> d['room1']['t1'] # access ['t1'] inside nested 100
Q: Write an expression to compute the average temperature of average temp of t1 and t2 in room1?
Answer:
>>> # As above, this accesses one temp >>> d['room1']['t1'] 100 >>> >>> # Compute average >>> (d['room1']['t1'] + d['room1']['t2']) / 2 101.0
Can store a reference to the nested dict in a var, then use that. We typically do it this way.
>>> # Get a reference to room1 dict, store in var >>> temps = d['room1'] >>> (temps['t1'] + temps['t2']) / 2 101.0 >>>
For more detail see guide Python Tuples
>>> t = ('a', 13, 42) >>> t[0] 'a' >>> t[2] 42 >>> len(t) 3 >>> t[0] = 'b' TypeError: 'tuple' object does not support item assignment
Unfortunate syntax shortcut: possible to omit the parenthesis. We will not do this in CS106A code, but you can write it if you like and it is allowed under PEP8. We will write our code more spelled-out, showing explicitly where we have a tuple.
>>> t = 1, 4 # This works >>> t (1, 4) >>> t = (4, 5) # We prefer readable/spelled out >>> t (4, 5)
They look so similar, but have some real differences. When do you use a list and when a tuple?
Tuple assignment = shortcut. A way to assign multiple variables in one step.
>>> (x, y) = (3, 4) >>> x 3 >>> y 4
Revisit Dict, now that we have tuples
Say we have a dict loaded up with data
>>> d = {'a': 'alpha', 'g': 'gamma', 'b': 'beta'}
The following pattern we used before. This is a fine, standard pattern to use.
>>> for key in sorted(d.keys()): ... print(key, d[key]) ... a alpha b beta g gamma
That said, there are some other ways to do it.
keys() values() items()
>>> d = {'a': 'alpha', 'g': 'gamma', 'b': 'beta'} >>> >>> d.keys() dict_keys(['a', 'g', 'b']) >>> sorted(d.keys()) ['a', 'b', 'g'] >>> >>> d.values() dict_values(['alpha', 'gamma', 'beta']) >>> >>> d.items() # still random order dict_items([('a', 'alpha'), ('g', 'gamma'), ('b', 'beta')]) >>>
>>> cities = [('tx', 'houston'), ('ca', 'palo alto'), ('ca', 'san jose'), ('tx', 'austin'), ('ca', 'aardvark')] >>> >>> sorted(cities) [('ca', 'aardvark'), ('ca', 'palo alto'), ('ca', 'san jose'), ('tx', 'austin'), ('tx', 'houston')] >>> >>> sorted(cities, reverse=True) [('tx', 'houston'), ('tx', 'austin'), ('ca', 'san jose'), ('ca', 'palo alto'), ('ca', 'aardvark')]
Recall that d.items()
is a list of len-2 key/value tuples:
>>> d.items() [('a', 'alpha'), ('g', 'gamma'), ('b', 'beta')]
Since sorting of tuples goes by [0] first, and [0] here is the key, the len-2 tuples are in effect sorted by key:
>>> sorted(d.items()) [('a', 'alpha'), ('b', 'beta'), ('g', 'gamma')]
The keys are all unique, so the sorting never looks at the [1] values.
>>> for item in sorted(d.items()): ... print(item[0], item[1]) ... a alpha b beta g gamma
Recall the shortcut
>>> (a, b) = (6, 7) >>> a 6 >>> b 7
Can use a similar shortcut inside a for loop. Since we are looping over tuples len-2, can specify two variables, and the loop unpacks each tuple into the variables, here key and value:
>>> for key, value in sorted(d.items()): ... print(key, value) ... a alpha b beta g gamma
Map - a short way to transform a list - handy, but not super important
Lambda - an important way to package some code. Today we'll use map() to explore how lambda works
Lambda code is dense. Another way of saying that it is powerful. Sometimes you feel powerful with computer code because the code you write is long. Sometimes you feel even a little more powerful, because the code you write is short!
There is something satisfying about solving a real problem with 1 line of code. The 1-liner code is so dense, we'll will write it a little more deliberately. See how this works below!
Consider the following "double" def. What does this provide to the rest of the program?
def double n: return n * 2
The def sets up the name of the function, and associates it with that body of code. Later line can refer to this function by name. The drawing below shows a form of this - the name "double" now points to this black-box of code that anybody can call.
Answer: takes in one parameter value. Returns one value.
>>> # Normally don't def a function in the interpreter. >>> # But it works for little demos like this. >>> >>> def double(n): ... return n * 2 ... >>> >>> double(10) 20 >>> double(144) 288
A visual of what map() does
map(double, [1, 2, 3, 4, 5]) -> [2, 4, 6, 8, 10]
>>> # We have a "double" def >>> def double(n): ... return n * 2 ... >>> >>> map(double, [1, 2, 3, 4, 5])
Say we have an exclaim(s)
function that takes in a string and returns it uppercase with an exclamation mark at the end. Use map to run exclaim() over a list of strings.
>>> def exclaim(s): ... return s.upper() + '!' ... >>> >>> list(map(exclaim, ['hi', 'woot', 'donut'])) ['HI!', 'WOOT!', 'DONUT!'] >>> >>> list(map(exclaim, ['meh'])) ['MEH!']
Enter the Lambda
Here is a lambda that takes in a number, returns double that number
lambda n: 2 * n
It's like the lambda just defines the black box code, not bothering with giving it name.
Want to double a bunch of numbers? Instead of a separate def, write the lambda inside the map() like this:
>>> list(map(lambda n: n * 2, [1, 2, 3, 4, 5])) [2, 4, 6, 8, 10]
Write the word "lambda"
Do these in interpreter >>>. Just hit the up-arrow to change the body of the lambda.
>>> nums = [1, 2, 3, 4] >>> >>> # n * 10 >>> list(map(lambda n: n * 10, nums)) [10, 20, 30, 40] >>> >>> # n * -1 >>> list(map(lambda n: n * -1, nums)) [-1, -2, -3, -4] >>> >>> # 100 - n >>> list(map(lambda n: 100 - n, nums)) [99, 98, 97, 96] >>> >>>
Have a list of strings. Map a lambda over this list. What is the parameter to the lambda? One string. Whatever the lambda returns, that's what makes up the list of results.
>>> strs = ['Banana', 'apple', 'Zebra', 'coffee', 'Donut'] >>> >>> list(map(lambda s: s.lower(), strs)) ['banana', 'apple', 'zebra', 'coffee', 'donut'] >>> >>> list(map(lambda s: s[0], strs)) ['B', 'a', 'Z', 'c', 'D'] >>> >>> # Works with strings - change param name to "s" >>> list(map(lambda s: s.upper() + '!', ['hi', 'ho', 'meh'])) ['HI!', 'HO!', 'MEH!'] >>>
These are true one-liner exercises. We'll do a few of them in class, and you can look at the others in the lambda1 section.
Solve these with a 1-line call to map() for each. Do not call list(), that was needed in the interpreter, but here just map() works.
> lambda1 exercises
For reference, here is the syntax for our "double" example:
map(lambda n: 2 * n, [1, 2, 3, 4, 5])
Do these: squared(), diff21() (int)
Then strings: first2x(), first_up() (str)