A dict
is a mapping of key-value pairs
states = {
"CA": "California",
"DE": "Delaware",
"NY": "New York",
"TX": "Texas",
"WY": "Wyoming"
}
Dictionaries support similar operations as lists/strings:
>>> len(states)
5
>>> "CA" in states
True
>>> "ZZ" in states
False
words = {
"más": "more",
"otro": "other",
"agua": "water"
}
Ways to access a value by key:
>>> words["otro"]
'other'
>>> first_word = "agua"
>>> words[first_word]
'water'
>>> words["pavo"]
KeyError: pavo
>>> words.get("pavo", "🤔")
'🤔'
spiders = {
"smeringopus": {
"name": "Pale Daddy Long-leg",
"length": 7
},
"holocnemus pluchei": {
"name": "Marbled cellar spider",
"length": (5, 7)
}
}
insects = {"spiders": 8, "centipedes": 100, "bees": 6}
for name in insects:
print(insects[name])
What will be the order of items?
8 100 6
Keys are iterated over in the order they are first added.
General syntax:
{key: value for <name> in <iter exp>}
Example:
{x: x*x for x in range(3,6)}
def prune(d, keys):
"""Return a copy of D which only contains key/value pairs
whose keys are also in KEYS.
>>> prune({"a": 1, "b": 2, "c": 3, "d": 4}, ["a", "b", "c"])
{'a': 1, 'b': 2, 'c': 3}
"""
def prune(d, keys):
"""Return a copy of D which only contains key/value pairs
whose keys are also in KEYS.
>>> prune({"a": 1, "b": 2, "c": 3, "d": 4}, ["a", "b", "c"])
{'a': 1, 'b': 2, 'c': 3}
"""
return {k: d[k] for k in keys}
def index(keys, values, match):
"""Return a dictionary from keys k to a list of values v for which
match(k, v) is a true value.
>>> index([7, 9, 11], range(30, 50), lambda k, v: v % k == 0)
{7: [35, 42, 49], 9: [36, 45], 11: [33, 44]}
"""
def index(keys, values, match):
"""Return a dictionary from keys k to a list of values v for which
match(k, v) is a true value.
>>> index([7, 9, 11], range(30, 50), lambda k, v: v % k == 0)
{7: [35, 42, 49], 9: [36, 45], 11: [33, 44]}
"""
return {k: [v for v in values if match(k, v)] for k in keys}
Many useful way to combine lists and dicts:
Lists of lists | [ [1, 2], [3, 4] ] |
Dicts of dicts | {"name": "Brazilian Breads", "location": {"lat": 37.8, "lng": -122}} |
Dicts of lists | {"heights": [89, 97], "ages": [6, 8]} |
Lists of dicts | [{"title": "Ponyo", "year": 2009}, {"title": "Totoro", "year": 1993}] |
Slicing a list creates a new list with a subsequence of the original list.
letters = ["A", "B", "C", "D", "E", "F"]
# 0 1 2 3 4 5
sublist1 = letters[1:] # ['B', 'C', 'D', 'E', 'F']
sublist2 = letters[1:4] # ['B', 'C', 'D']
Slicing also works for strings.
compound_word = "cortaúñas"
word1 = compound_word[:5] # "corta"
word2 = compound_word[5:] # "úñas"
Negatives indices and steps can also be specified.
Slicing a whole list copies a list:
listA = [2, 3]
listB = listA
listC = listA[:]
listA[0] = 4
listB[1] = 5
list()
creates a new list containing existing elements from any iterable:
listA = [2, 3]
listB = listA
listC = list(listA)
listA[0] = 4
listB[1] = 5
Python3 provides more ways in the copy module.
The following built-in functions work for lists, strings, dicts, and any other iterable data type.
Function | Description |
---|---|
sum(iterable, start) |
Returns the sum of values in iterable , initializing sum to start
|
all(iterable) |
Return True if all elements of iterable are true (or if iterable is empty)
|
any(iterable) |
Return True if any element of iterable is true. Return False if iterable is empty.
|
max(iterable, key=None) |
Return the max value in iterable
|
min(iterable, key=None) |
Return the min value in iterable
|
sum([73, 89, 74, 95], 0) # 331
all([True, True, True, True]) # True
any([False, False, False, True]) # True
all([x < 5 for x in range(5)]) # True
perfect_square = lambda x: x == round(x ** 0.5) ** 2
any([perfect_square(x) for x in range(50, 60)]) # False
max([73, 89, 74, 95]) # 95
max(["C+", "B+", "C", "A"]) # C+
max(range(10)) # 9
A key function can decide how to compare each value:
coords = [ [37, -144], [-22, -115], [56, -163] ]
max(coords, key=lambda coord: coord[0]) # [56, -163]
min(coords, key=lambda coord: coord[0]) # [-22, -115]
gymnasts = [ ["Brittany", 9.15, 9.4, 9.3, 9.2],
["Lea", 9, 8.8, 9.1, 9.5],
["Maya", 9.2, 8.7, 9.2, 8.8] ]
min(gymnasts, key=lambda scores: min(scores[1:])) # ["Maya", ...]
max(gymnasts, key=lambda scores: sum(scores[1:], 0)) # ["Brittany", ...]
If a recursive function needs to keep track of more state than the arguments of the original function, you may need a helper function.
def fUnKyCaSe(text):
"""
>>> fUnKyCaSe("wats up")
'wAtS Up'
"""
def toggle_case(letter, should_up_case):
return letter.upper() if should_up_case else letter.lower()
def up_down(text, should_up_case):
if len(text) == 1:
return toggle_case(text, should_up_case)
else:
return toggle_case(text[0], should_up_case) + \
up_down(text[1:], not should_up_case)
return up_down(text, False)
Let's code this up recursively:
def sum_nums(nums):
"""Returns the sum of the numbers in NUMS.
>>> sum_nums([6, 24, 1984])
2014
>>> sum_nums([-32, 0, 32])
0
"""
Docstrings typically would not specify whether an approach was recursive or iterative, since that is an implementation detail.
However, we'll make it clear in assignments and exam questions.
def sum_nums(nums):
"""Returns the sum of the numbers in NUMS.
>>> sum_nums([6, 24, 1984])
2014
>>> sum_nums([-32, 0, 32])
0
"""
if nums == []:
return 0
else:
return nums[0] + sum_nums( nums[1:] )
When recursively processing lists, the base case is often the empty list and the recursive case is often all-but-the-first items.
def reverse(s):
"""Returns a string with the letters of S
in the inverse order.
>>> reverse('ward')
'draw'
"""
Breaking it down into subproblems:
reverse("ward") = reverse("ard") + "w"
reverse("ard") = reverse("rd") + "a"
reverse("rd") = reverse("d") + "r"
reverse("d") = "d"
def reverse(s):
"""Returns a string with the letters of S
in the inverse order.
>>> reverse('ward')
'draw'
"""
if len(s) == 1:
return s
else:
return reverse(s[1:]) + s[0]
When recursively processing strings, the base case is typically an empty string or single-character string, and the recursive case is often all-but-the-first characters.
def reverse(n):
"""Returns N with the digits reversed.
>>> reverse_digits(123)
321
"""
Data type | Base case condition | Current item | Recursive case argument |
---|---|---|---|
Numbers | == 0 == 1
| n
| n - 1
|
Numbers (Digits) | == 0
< 10
| n % 10
| n // 10
|
Lists | == [] len(L) == 0
| L[0] L[-1]
| L[1:] L[:-1]
|
Strings | == '' len(S) == 1
| S[0]
| S[1:] S[:-1]
|
Lists are represented as a row of index-labeled adjacent boxes, one per element.
pair = [1, 2]
Each box either contains a primitive value or points to a compound value.
matrix = [ [1, 2, 0, 4], [0, 1, 3, -1], [0, 0, 1, 8] ]
A very nested list:
worst_list = [ [1, 2],
[],
[ [3, False, None], [4, lambda: 5]]]
Sea Level Rise, by Douwe Osinga: Visualize sea levels and population density on interactive maps.
Technologies used: Python (notebook) with PIL/numpy/Rasterio, HTML/CSS/JS with PanZoom
(Github repository)