Arrays
Definition
Arrays in python are of list
type that is dynamically resized. The tuple
is similar but immutable.
Basic operations
# Manipulate list
A = list(range(10))
print(f'len() of A: {len(A)}')
A.append(5)
print(f'append() to A: {A}')
A.remove(5)
print(f'remove() first occurence from A: {A}')
A.insert(1, 999)
print(f'insert() first occurence from A: {A}')
print(f'min() of A: {min(A)}')
print(f'max() of A: {max(A)}')
del A[1]
print(f'del element with index i of A: {A}')
del A[:4]
print(f'del a range of elements of A: {A}')
len() of A: 10
append() to A: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 5]
remove() first occurence from A: [0, 1, 2, 3, 4, 6, 7, 8, 9, 5]
insert() first occurence from A: [0, 999, 1, 2, 3, 4, 6, 7, 8, 9, 5]
min() of A: 0
max() of A: 999
del element with index i of A: [0, 1, 2, 3, 4, 6, 7, 8, 9, 5]
del a range of elements of A: [4, 6, 7, 8, 9, 5]
Copy
3 types:
- assignment copy(=): makes the two variables point to the one list in memory
- shallow copy ([:] OR copy.copy()): constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original
- deep copy (copy.deepcopy()): constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original. Slower but changes will NOT be reflected on the original list
Difference between shallow and deep copying is only relevant for compound objects (e.g. a list of lists, or class instances).
Reference: https://medium.com/@thawsitt/assignment-vs-shallow-copy-vs-deep-copy-in-python-f70c2f0ebd86
# Assignment copy
colors = ['red', 'blue', 'green']
b = colors
b.append('white')
print(f'Copied/modified list: {b}')
print(f'Original list ALSO modified: {colors}')
Copied/modified list: ['red', 'blue', 'green', 'white']
Original list ALSO modified: ['red', 'blue', 'green', 'white']
# Shallow copy
a = [[1, 2], [2, 4]]
b = a[:] ## shallow copy
b.append([3, 6])
print(f'Copied/modified list: {b}')
print(f'Original list NOT modified: {a}')
Copied/modified list: [[1, 2], [2, 4], [3, 6]]
Original list NOT modified: [[1, 2], [2, 4]]
# Shallow copy compound object => referenced to the original elements
a = [[1, 2], [2, 4]]
b = a[:] #shallow copy
b[0].append(3)
print(f'Copied/modified list: {b}')
print(f'Original list ALSO modified: {a}')
# List b has its own pointer, but its elements do not
Copied/modified list: [[1, 2, 3], [2, 4]]
Original list ALSO modified: [[1, 2, 3], [2, 4]]
# Deep copy, need copy module
import copy
a = [[1, 2], [2, 4]]
b = copy.deepcopy(a) ## deep copy
b[0].append(3)
print(f'Copied/modified list: {b}')
print(f'Original list NOT modified: {a}')
Copied/modified list: [[1, 2, 3], [2, 4]]
Original list NOT modified: [[1, 2], [2, 4]]
Binary search for sorted list
bisect
module maintains a list in sorted order without having to sort the list after each insertion
# index where the element needs to be inserted in a sorted list
import bisect
A = list(range(10))
print(A)
bisect.bisect(A, 6) # similar to bisect.bisect_right()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7
# if element exist, return index of the left of it
print(A)
bisect.bisect_left(A, 6) # similar to bisect.bisect_right()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6
# insert the element and not just the index
A = list(range(10))
print(A)
bisect.insort(A, 7) # same as bisect.insort_right()
print(A)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9]
Sorting
# reverse in-place
A = list(range(10))
A.reverse()
print(A)
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# return an iterator
A = list(range(10))
for i in reversed(A): print(i, end=',')
9,8,7,6,5,4,3,2,1,0,
# sort in-place
A = [4,3,2,1,0]
A.sort()
print(A)
[0, 1, 2, 3, 4]
# returns a copy
A = [4,3,2,1,0]
sorted(A)
[0, 1, 2, 3, 4]
Slicing
# note the range is [0,5)
A = list(range(10))
A[0:5]
[7, 8]
# from the back
A[-3:-1]
[7, 8]
# skip n=2
A[0:5:2]
[0, 2, 4]
# reverse with [::-1]
A[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
# rotate a list
A[5:] + A[:5]
[5, 6, 7, 8, 9, 0, 1, 2, 3, 4]
List comprehension
[<expression> for element in <iterator over sequence> <logical condition>]
[x**2 for x in range(6) if x%2==0]
[0, 4, 16]
# multiple levels
A = [1,2,3]
B = ['a', 'b']
[(x,y) for x in A for y in B]
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b')]
# convert 2D to 1D
M = [['a', 'b', 'c'],['d', 'e']]
[x for row in M for x in row]
['a', 'b', 'c', 'd', 'e']
# 2 level looping
M = [[1,2,3], [4,5,6]]
[[x**2 for x in row] for row in M]
[[1, 4, 9], [16, 25, 36]]
Useful tips
- Use of
next(iterator[, default])
# remove the leading zeros
res = [0,0,1,2,3,4]
res[next((i for i,x in enumerate(res) if x!=0)):]
[1, 2, 3, 4]
# what if they are all zeros
res = [0,0,0,0,0]
res[next((i for i,x in enumerate(res) if x!=0), len(res)):]
[]
for-else
statement: search through a list and process each item until a flag item is found and then stop processing
# Loop until something is found
mylist=[1,2,3]
for i in mylist:
if i == 5:
break
print(i)
else:
raise ValueError("List argument missing terminal flag.")
1
2
3
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-1-9dbebae898f3> in <module>
6 print(i)
7 else:
----> 8 raise ValueError("List argument missing terminal flag.")
ValueError: List argument missing terminal flag.
get() dict
dictionary.get(keyname, value)
returns the value of the item with the specified key and value if key does not exist
car = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}
x = car.get("price", 15000)
print(x)
15000
any()
and all()
any()
sequence of OR operations on the provided iterables. Stops the execution as soon as the result is known.all()
sequence of AND operations on the provided iterables. Stops the execution as soon as the result is known.
# short circuits after second element
print (any([False, True, False, False]))
True
# short circuits at the first element
print (all([False, True, True, False]))
False
Checking for duplicates
def has_duplicates(block):
return len(block) != len(set(block))
a=[1,2,3,1]
print(has_duplicates(a))
True
Filter + lambda vs. list comprehension
Time is very close between the two, thought list comprehension slighlty faster.
import timeit
block=[1,0,3,1,0]
time1=timeit.timeit(str(list(filter(lambda x: x != 0, block))), number=100000000)
print(time1)
time2=timeit.timeit(str([x for x in block if x != 0]), number=100000000)
print(time2)
3.8697273540000197
3.6555018579999796
Loop over sub-blocks of a matrix
Useful when doing convolutions
# Example with 9x9 matrix and sub-block of 3x3
import math
M = [[0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 2, 0, 0], [0, 0, 0, 0, 6, 0, 4, 0, 0], [0, 0, 0, 2, 0, 0, 0, 0, 5], [7, 0, 0, 0, 0, 0, 0, 0, 0], [0, 8, 3, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 3, 3, 0], [0, 0, 4, 0, 0, 0, 0, 0, 0]]
m = len(M)
n = int(math.sqrt(m))
# Original matrix
print('Original matrix:')
for i in range(m):
for j in range(m):
print(M[i][j], end=', ')
print('')
print('---')
print('Sub-blocks of the matrix:')
for i in range(n):
for j in range(n):
for a in range(i*n, (i+1)*n):
for b in range(j*n, (j+1)*n):
print(M[a][b], end=',')
print('')
print('---')
Original matrix:
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 2, 0, 0,
0, 0, 0, 0, 6, 0, 4, 0, 0,
0, 0, 0, 2, 0, 0, 0, 0, 5,
7, 0, 0, 0, 0, 0, 0, 0, 0,
0, 8, 3, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 3, 3, 0,
0, 0, 4, 0, 0, 0, 0, 0, 0,
---
Sub-blocks of the matrix:
0,0,0,
0,0,0,
0,0,0,
---
0,0,0,
0,0,0,
0,0,1,
---
0,0,0,
0,0,0,
2,0,0,
---
0,0,0,
0,0,0,
7,0,0,
---
0,6,0,
2,0,0,
0,0,0,
---
4,0,0,
0,0,5,
0,0,0,
---
0,8,3,
0,0,0,
0,0,4,
---
0,0,0,
0,0,0,
0,0,0,
---
0,0,0,
3,3,0,
0,0,0,
---