0% found this document useful (0 votes)
15 views

Lists

Uploaded by

xidola8815
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
15 views

Lists

Uploaded by

xidola8815
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 30

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 = [13, 27, 7, 42] a = [13, 27, 7, 42]


b = a b = a[:]
a 13 a[0]
a[2] = 12 a[2] = 12 27 a[1]
b
12-
7 a[2]
a 13 a[0]
42 a[3]
27 a[1]
12-
7 a[2]
b 13 b[0]
42 a[3]
27 b[1]
7 b[2]
42 b[3]
x[:] vs nested structures 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) 1:1 1:2 1:3 2:1 2:2 2:3 3:1 3:2 3:3


b) 1:1 1:2 1:3 2:2 2:3 3:3
c) 1:1 2:1 3:1 1:2 2:2 3:2 1:3 2:3 3:3
d) 1:1 2:1 3:1 2:2 3:2 3:3
e) Don’t know
Question – break, what is printed ?
Python shell
> for i in range(1, 4):
> for j in range(1, 4):
> print(i, j, sep=':', end=' ')
> if j >= i:
> break

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)

for i in range(2, n):


▪ Algorithm: for j in range(2 * i, n + 1, i):
prime[j] = False
2 3 4 5 6 7 8 9 10 11 12 13 14 ...
for i in range(2, n + 1):
2 3 4 5 6 7 8 9 10 11 12 13 14 ... if prime[i]:
2 3 4 5 6 7 8 9 10 11 12 13 14 ... print(i, end=' ')
2 3 4 5 6 7 8 9 10 11 12 13 14 ... Python shell
2 3 4 5 6 7 8 9 10 11 12 13 14 ... | 2 3 5 7 11 13 17 19 23 29 31 37 41
43 47 53 59 61 67 71 73 79 83 89
2 3 4 5 6 7 8 9 10 11 12 13 14 ... 97

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

▪ The “else” block is only executed if no break is performed in the loop


▪ The “else” construction for loops is specific to Python,
and does not exist in e.g. C, C++ and Java
Linear search linear-search-for.py
linear-search-while.py found = False
for i in range(len(L)):
L = [7, 3, 6, 4, 12, 'a', 8, 13]
if L[i] == x:
x = 4
print(x, 'at position', i, 'in', L)
found = True
i = 0
break
while i < len(L):
if L[i] == x:
if not found:
print(x, 'at position', i, 'in', L)
print(x, 'not in', L)
break
i = i + 1
linear-search-for-else.py
if i >= len(L): for i in range(len(L)):
print(x, 'not in', L) if L[i] == x:
print(x, 'at position', i, 'in', L)
linear-search-while-else.py break
else:
i = 0 print(x, 'not in', L)
while i < len(L):
if L[i] == x:
linear-search-builtin.py
print(x, 'at position', i, 'in', L)
break if x in L:
i = i + 1 print(x, 'at position', L.index(x), 'in', L)
else: else:
print(x, 'not in', L) print(x, 'not in', L)
Some performance considerations
Python shell
String concatenation > s = 'A' + 'B' + 'C'
> s
| 'ABC'
> 'x'.join(['A', 'B', 'C'])
▪ To concatenate two (or few) strings use | 'AxBxC'
> s = ''
str1 + str2 > s += 'A'
var += str > s += 'B'
> s += 'C'
> s
▪ To concatenate several/many strings use | 'ABC'
> L = []
''.join([str1, str2, str3, ... , strn]) > L.append('A')
> L.append('B')
▪ Concatenating several strings by repeated > L.append('C')
> L
use of + generates explicitly the longer- | ['A', 'B', 'C']
and-longer intermediate results; using > s = ''.join(L)
> s
join avoids this slowdown | 'ABC'
string-concatenation.py
from time import time
from matplotlib import pyplot as plt
ns = range(10_000, 1_000_000, 10_000)
time_string = []
time_list = []
for n in ns:
start = time()
s = ''
≈ 4∙10-12 ∙ n2 for _ in range(n):
s += 'abcdefgh' # slow
end = time()
time_string.append(end - start)
start = time()
substrings = []
for _ in range(n):
substrings.append('abcdefgh');
s = ''.join(substrings);
end = time()
time_list.append(end - start)
≈ 2∙10-7 ∙ n plt.plot(ns, time_string, label='string +=')
plt.plot(ns, time_list, label="''.join(list)")
plt.xlabel('n')
plt.ylabel('time')
plt.legend()
plt.show()
The internal implementation of Python lists
▪ Accessing and updating list positions take the same time independently of position
▪ Creating new / deleting entries in a list depends on position,
Python optimizes towards updates at the end
▪ Try to organize your usage of lists to insert / delete elements at the end
L.append(element) and L.pop()
▪ Python lists internally have space for adding ≈ 12.5 % additional entries at the end;
when the reserved extra space is exhausted the list is moved to a new chunk of
memory with ≈ 12.5 % extra space
0 i len(L) - 1
memory L L reserve

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

ns = range(0, 1_000_001, 10_000)


time_pos = []
L = list(range(1_000_000)) # L = [0, ..., 999_999]
for i in ns:
start = time()
updates at for _ in range(1000):
front of list updates at L[i:i] = [42] # insert element before L[i]
end of list del L[i] # remove L[i] from L
end = time()
time_pos.append(end - start)

plt.plot(ns, time_pos)
plt.xlabel('position')
plt.ylabel('time')
plt.show()

You might also like