Lists
Lists
▪ List syntax
▪ List operations
▪ copy.deepcopy
▪ range
▪ while-else
▪ for
▪ for-break-continue-else
List operations
▪ List syntax [value0, value1, ..., valuek-1]
▪ List indexing L[index], L[-index]
▪ List slices L[from:to], L[from:to:step] or L[slice(from,to,step)]
▪ Creating a copy of a list L[:] or L.copy()
▪ List concatenation (creates new list) X + Y
▪ List repetition (repeated concatenation with itself) 42 * L
▪ Length of list len(L)
▪ Check if element is in list e in L (returns True or False)
▪ Check if element is not in list e not in L (same as not e in L)
▪ Index of first occurrence of element in list L.index(e)
▪ Number of occurrences of element in list L.count(e)
▪ sum(L) min(L) max(L)
= creates new list docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
sum(…)
Python shell
> 1 - 1/3 - 1 + 1/3 # mathematically should be zero
| 5.551115123125783e-17 # but floats are imprecise
> L = [1, -1/3, -1, 1/3]
> L
| [1, -0.3333333333333333, -1, 0.3333333333333333] # mix of int and float
> sum(L)
| 5.551115123125783e-17
> sum([1.0, -1/3, -1.0, 1/3]) # all floats
| 5.551115123125783e-17 # Python 3.11
> sum([1.0, -1/3, -1.0, 1/3])
| 0.0 # Python 3.12 uses "Neumaier summation" to improve accuracy for floats
> sum([1, -1/3, -1, 1/3])
| 5.551115123125783e-17 # Python 3.12 looses accuracy when mixing int and float
> import math
> math.fsum([1, -1/3, -1, 1/3]) # math.fsum more accurate float sums
| 0.0
List modifiers (lists are mutable)
▪ Extend list with elements (X is modified) X.extend(Y)
▪ Append an element to a list (L is modified) L.append(42)
▪ Replace sublist by another list (length can differ) X[i:j] = Y
▪ Delete elements from list del L[i:j:k]
▪ Remove & return element at position L.pop(i) Python shell
▪ Remove first occurrence of element L.remove(e)
> x = [1, 2, 3, 4, 5]
▪ Reverse list L.reverse() > x[2:4] = [10, 11, 12]
> x
▪ L *= 42 | [1, 2, 10, 11, 12, 5]
> x = [1, 2, 11, 5, 8]
▪ L.insert(i, x) same as L[i:i] = [x]
> x[1:4:2] = ['a', 'b']
| [1, 'a', 11, 'b', 8]
docs.python.org/3/library/stdtypes.html#sequence-types-list-tuple-range
Questions – What is x ?
x = [1,2,3,4,5,6,7,8,9,10]
x[2:8:3] = ['a', 'b']
a) [1,2,'a','b',5,6,7,8,9,10]
b) [1,'a',3,4,5,6,7,'b',9,10]
c) [1,2,3,4,5,6,7,'a','b']
d) [1,2,'a',4,5,'b',7,8,9,10]
e) ValueError
f) Don’t know
Questions – What is y ?
y = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
y = y[3:15:3][1:4:2]
a) [3,6,9,12,15]
b) [7,13]
c) [1,9]
d) [4,7,10,13,2,4]
e) TypeError
f) Don’t know
Nested lists (multi-dimensional lists)
multidimensional-lists.py
▪ Lists can contain lists as elements, list1d = [1, 3, 5, 2]
that can contain lists as elements, list2d = [[1, 2, 3, 4],
that ... [5, 6, 7, 9],
[0, 8, 2, 3]]
▪ Can e.g. be used to store multi- list3d = [[[5,6], [4,2], [1,7], [2,4]],
[[1,2], [6,3], [2,5], [7,5]],
dimensional data (list lengths can [[3,8], [1,5], [4,3], [2,4]]]
be non-uniform)
print(list1d[2])
print(list2d[1][2])
print(list3d[2][0][1])
Note: For dealing with matrices the
Python shell
NumPy module is a better choice |5
|7
|8
aliasing
Memory
b
a = [13, 27, 7, 42]
a 13 a[0]
b = a 27 a[1]
a[2] = 12 12-
7 a[2]
42 a[3]
y = x vs y = x[:]
Memory Memory
a a[0]
b a[1]
a = [[3,5],[7,11]]
3
b = a
4-
5
c = a[:]
a[0][1] = 4 7
c[1] = b[0] 11
-
c c[0]
c[1]
Question – what is c ? Memory
a) [[3,5],[7,11]] a a[0]
b a[1]
b) [[3,5],[3,5]]
a = [[3,5],[7,11]]
c) [[3,4],[3,5]] b = a
3
4-
5
d) [[3,4],[3,4]] c = a[:]
e) Don’t know a[0][1] = 4 7
c[1] = b[0] 11
-
c c[0]
c[1]
Try pythontutor.com
copy.deepcopy Memory
a a[0]
▪ To make a copy of all parts of a composite value 7 a[1]
use the function deepcopy from module copy
4-
3
5
Python shell
> from copy import deepcopy
> a = [[3, 5], 7] b b[0]
> b = deepcopy(a) 7 b[1]
> a[0][0] = 4
> a
| [[4,5],7] 3
> b 5
| [[3,5],7]
Initializing a 2-dimensional list
Python shell
> x = [1] * 3
> x
| [1, 1, 1]
> y = [[1] * 3] * 4
> y
| [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
> y[0][0] = 0
> y
| [[0, 1, 1], [0, 1, 1], [0, 1, 1], [0, 1, 1]]
Python shell
> y = []
> for _ in range(4): y.append([1] * 3)
> y[0][0] = 0
> y
| [[0, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]
In Python 2, range generates the explicit list,
i.e. always use memory proportional to the
range(from, to, step) length; xrange in Python 2 corresponds to range
in Python 3; Python 3 is more memory friendly
▪ range(from, to, else) represents the sequence of numbers starting with from,
with increments of step, and smaller/greater than to if step positive/negative
range(5) : 0, 1, 2, 3, 4 (default from = 0, step = 1)
range(3, 8) : 3, 4, 5, 6, 7 (default step = 1)
range(-2, 7, 3) : -2, 1, 4 (from and to can be any integer)
range(2, -5, -2) : 2, 0, -2, -4 (decreasing sequence if step negative)
▪ Ranges are immutable, can be indexed Python shell
like a list, sliced, and compared > range(1, 10000000, 3)[2]
(i.e. generate the same numbers) |7
> range(1, 10000000, 3)[100:120:4]
▪ list(range(...)) generates the | range(301, 361, 12)
explicit list of numbers > range(1, 10000000, 3)[100:120:4][2:3]
| range(325, 337, 12)
> list(range(5, 14, 3))
| [5, 8, 11]
Question – What is range(3,20,4)[2:4][1] ?
a) 3
b) 7
c) 11
d) 15
e) 19
f) Don’t know
for - loop Python shell
> for x in [1, 'abc', [2, 3], 5.0]:
▪ For every element in a sequence > print(x)
execute a block of code: |1
| abc
for var in sequence: | [2, 3]
| 5.0
block > for x in 'abc':
> print(x)
▪ Sequences can e.g. be lists, strings, |a
ranges |b
|c
▪ break and continue can be used > for x in range(5, 15, 3):
like in a while-loop to break out of the > print(x)
for-loop or continue with the next |5
element in the sequence |8
| 11
| 14
Question – What is printed ?
Python shell
> for i in range(1, 4):
> for j in range(i, 4):
> print(i, j, sep=':', end=' ')
a) nothing
b) 1:1
c) 1:1 2:1 2:2 3:1 3:2 3:3
d) 1:1 2:2 3:3
e) Don’t know In nested for- and while-loops,
break only breaks the innermost loop
Palindromic substrings palindrom.py
s = 'abracadrabratrallalla'
▪ Find all palindromic substrings of for i in range(len(s)):
length ≥ 2, i.e. substrings spelled for j in range(i + 2, len(s) + 1):
identically forward and backwards: t = s[i:j]
if t == t[::-1]:
abracadrabratrallalla print(t)
i j i j Python shell
| aca
| alla
| allalla
▪ Algorithm: Test all possible substrings | ll
(brute force/exhaustive search) | llall
| lal
▪ Note: the slice t[::-1] is t reversed | alla
| ll
Sieve of Eratosthenes
eratosthenes.py
▪ Find all prime numbers ≤ n n = 100
prime = [True] * (n + 1)
en.wikipedia.org/wiki/Sieve_of_Eratosthenes
while-else and for-else loops
▪ Both for- and while-loops can have an optional “else”:
for var in sequence:
block
else:
block
while condition:
block
else:
block
move right
L[i:i] = [42] will move all next cell to be used
trailing cells one position to right by L.append
github.com/python/cpython/blob/master/Objects/listobject.c
list-insertions.py
List insertions at front vs end from time import time
from matplotlib import pyplot as plt
ns = range(1000, 100_000, 1000)
time_end = []
time_append = []
time_front = []
for n in ns:
≈ 7∙10-10 ∙ n2 start = time()
L = []
for i in range(n):
L[i:i] = [i] # insert after list
end = time()
time_end.append(end - start)
start = time()
L = []
for i in range(n):
L.append(i) # append to list
end = time()
time_append.append(end - start)
start = time()
L = []
for i in range(n):
L[0:0] = [i] # insert at front
end = time()
≈ 3∙10-7 ∙ n time_front.append(end - start)
≈ 2∙10-7 ∙ n plt.plot(ns, time_front, label='front')
plt.plot(ns, time_append, label='append')
plt.plot(ns, time_end, label='end')
plt.xlabel('n')
plt.ylabel('time')
plt.legend()
plt.show()
Updates (insertions + deletions) in the middle of a list
list-updates.py
from time import time
from matplotlib import pyplot as plt
plt.plot(ns, time_pos)
plt.xlabel('position')
plt.ylabel('time')
plt.show()