Today: tuples, dict.items() output pattern, lambda, map, one-liners

File unicode error: if you get a UnicodeDecodeError, change open() as below. Most files are encoded with 'utf-8', but in some cases (Windows?) Python is not using that as the default. Can specify 'utf-8' in the open() line.

with open(filename, 'r', encoding='utf-8') as f:
    ....

Dict Nest Exercise

Have dict with nested dicts under the 'a' and 'b' keys. Nested dicts both have values under key 1, like the 100 and 200 here:

>>> d = {'a': {1: 100}, 'b': {1: 200}}
>>> 
>>> 
>>> d['a']
{1: 100}
>>> 
>>> 
>>> d['b']
{1: 200}
>>> 

Question: Using the 100 and 200 as placeholders, what lines of code will set the "100" value to be the larger of the "100" and "200" values?

Solution

# 1 line - not liking this style,
# too much going on for one line.
d['a'][1] = max(d['a'][1], d['b'][1])

# Decomp by var - vars point to nested dicts
adict = d['a']
bdict = d['b']
adict[1] = max(adict[1], bdict[1])
# Or could use an if-statement, check > of values

Tuples

>>> t = ('a', 13)
>>> t[0]
'a'
>>> t[1]
13
>>> len(t)
2
>>> t[0] = 'b'
TypeError: 'tuple' object does not support item assignment

Unfortunate syntax shortcut: possible to omit the parenthesis. We will never do this in CS106A code. More readable to spell out in the code that it's a tuple.

>>> t = 1, 4
>>> t
(1, 4)

Tuples vs. List

Tuples Assignment = Shortcut

Tuple assignment = shortcut. A way to assign multiple variables in one step.

>>> (x, y) = (3, 4)
>>> x
3
>>> y
4

Sorting 1.0

>>> lst = [12, 19, 2, 1]
>>> sorted(lst)
[1, 2, 12, 19]
>>> 
>>> sorted(lst, reverse=True)
[19, 12, 2, 1]
>>> 
>>> lst = ['apple', 'zebra', 'banana']
>>> sorted(lst)
['apple', 'banana', 'zebra']

Sorting 2.0 - With Tuples

>>> pairs = [('stanford.edu', '/meh'), ('google.com','/search'),
  ('stanford.edu', '/admit'), ('google.com', '/aardvark')]
>>> pairs
[('stanford.edu', '/meh'), ('google.com', '/search'), ('stanford.edu', '/admit'), ('google.com', '/aardvark')]
>>> sorted(pairs)
[('google.com', '/aardvark'), ('google.com', '/search'), ('stanford.edu', '/admit'), ('stanford.edu', '/meh')]

Revisit Dict, now that we have tuples

Dict Load Up

Dict Data Out Pattern #1

Say we have a dict loaded up with data

>>> d = {'a': 'alpha', 'g': 'gamma', 'b': 'beta'}

The following pattern we've done many times, it works fine. You can still use it.

>>> for key in sorted(d.keys()):
...     print(key, d[key])
... 
a alpha
b beta
g gamma

But there is a more direct way to do it.

Dict Data Out: 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')])
>>> 
>>> sorted(d.items())  # we'll use this form
[('a', 'alpha'), ('b', 'beta'), ('g', 'gamma')]

Aside: Dict Asymmetry - Do Not Look Up By Value

Dict Output Pattern #1.5

>>> for item in sorted(d.items()):
...     print(item[0], item[1])
... 
a alpha
b beta
g gamma

Foreach "Unpack" Shortcut

d.items() is a list of tuples len-2. Instead of this:

for item in sorted(d.items()):
    print(item[0], item[1])

Can write this, much nicer:

for key, value in sorted(d.items()):
    print(key, value)

Dict Output Pattern #2

>>> for key, value in sorted(d.items()):
...     print(key, value)
... 
a alpha
b beta
g gamma

Lambda - Dense and Powerful

Thought for today: 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!

Consider this def. It has a name + code. What does this give the program? A name associated with some code that can be called, returns a computed value.

def double n:
    return n * 2

Lambda - Big Picture

Within many programs, there are spots where you have a list of xxx and need a list of yyy computed from xxx. The lambda technique here is an amazingly compact way of expressing that part of your program.

Lambda

-History: Alonzo Church 1930 Lambda Calculus

e.g. a lambda function that takes in n, returns double its value. Has: 1 param, code, returns double value. Does not have: name, "return", multiple lines

lambda n: n * 2

e.g. a lambda that takes a string and returns its int length * 10.

lambda s: len(s) * 10

Lambda in Action - map(fn, iterable)

Lambda Doubling Example

Double number lambda:

lambda n: n * 2
>>> nums = [1, 2, 3, 4]
>>> map(lambda n: n * 2, nums)

>>> list(map(lambda n: n * 2, nums))
[2, 4, 6, 8]

Lambda Num Examples

Do these in python3 interpreter >>> They're all 1 line long! (note: abs(n) is absolute value of n)

>>> nums = [1, 2, 3, 4]
>>> 
>>> list(map(lambda n: n * -1, nums))
[-1, -2, -3, -4]
>>> 
>>> list(map(lambda n: 100 - n, nums))
[99, 98, 97, 96]
>>>
>>> list(map(lambda n: abs(n - 3), nums))
[2, 1, 0, 1]
>>>

You Try It 1 - Map Lambda int

Solve 1.doubling and 2.squared with 1 line of code each. Do not call list(), that was just so the examples print in the interpreter.

> map-lambda

Lambda String Examples

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']
>>> 

You Try It 2 - Map Lambda str

Given list of strings, compute a new list with map/lambda. Lecture demo of one, students try the other 2.

Lambda / Def Interchange

The above examples all us lambda to express little functions. Just for completeness, the code of a def can be used instead of a lambda, like this:

>>> nums = [1, 2, 3, 4]
>>> def double(n):
...     return n * 2
... 
>>> list(map(double, nums))
[2, 4, 6, 8]

Where we would put a lambda, just put the name of the function. This is the rare case of referring to a function by name, but not putting () after it. The () calls the function, but here we want to identify the function by name but not call it.

If the code you want is several lines long, expressing it in a def is a better approach. Lambda's are best for truly short bits of code.