Lists are represented as a row of indexlabeled 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]]]
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.
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 allbutthefirst items.
Let's code this up iteratively:
def sum_up_to(n):
"""Returns the sum of positive numbers from 1 up to N (inclusive).
>>> sum_up_to(5)
15
"""
Using the range
type:
def sum_up_to(n):
"""Returns the sum of positive numbers from 1 up to N (inclusive).
>>> sum_up_to(5)
15
"""
sum = 0
for n in range(0, n+1):
sum += n
return sum
Remember that range(start, end)
always ends right before end
.
Now try it recursively:
def sum_up_to(n):
"""Returns the sum of positive numbers from 1 up to N (inclusive).
>>> sum_up_to(5)
15
"""
Now try it recursively:
def sum_up_to(n):
"""Returns the sum of positive numbers from 1 up to N (inclusive).
>>> sum_up_to(5)
15
"""
if n == 1:
return 1
else:
return n + sum_up_to(n1)
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 singlecharacter string, and the recursive case is often allbutthefirst characters.
def reverse(n):
"""Returns N with the digits reversed.
>>> reverse_digits(123)
321
"""
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):
"""Returns TEXT in fUnKyCaSe
>>> 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)
Data type  Base case condition  Current item  Recursive case argument 

Numbers  == 0 == 1
 n % 10
 n // 10

Lists  == []
 L[0]
 L[1:] L[:1]

Strings  == '' len(S) == 1
 S[0]
 S[1:] S[:1]

The following builtin functions work for sequence types (lists, strings, etc) 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", ...]
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)