Lists 2 - operators#

Download exercises zip#

There are several operators to manipulate lists. The following ones behave like the ones we’ve seen in strings:

Operator

Syntax

Result

Meaning

length

len(lst)

int

Return the list length

index

list[int]

obj

Reads/writes an element at the specified index

slice

list[int:int]

list

Extracts a sublist - return a NEW list

membership

obj in list

bool

Cheks if an object is contained in the list

concatenation

list + list

list

Concatenates two lists - return a NEW list

maximum

max(lst)

int

Given a list of numbers, return the greatest one

minimum

min(lst)

int

Given a list of numbers, returns the smallest one

sum

sum(lst)

int

Given a list of numbers, sums all of them

replication

list * int

list

Replicates the list - return a NEW list

equality

==,!=

bool

Cheks whether lists are equal of different

What to do#

  1. Unzip exercises zip in a folder, you should obtain something like this:

lists
    lists1.ipynb
    lists1-sol.ipynb
    lists2.ipynb
    lists2-sol.ipynb
    lists3.ipynb
    lists3-sol.ipynb
    lists4.ipynb
    lists4-sol.ipynb
    lists5-chal.ipynb
    jupman.py

WARNING: to correctly visualize the notebook, it MUST be in an unzipped folder !

  1. open Jupyter Notebook from that folder. Two things should open, first a console and then a browser. The browser should show a file list: navigate the list and open the notebook lists2.ipynb

  2. Go on reading the exercises file, sometimes you will find paragraphs marked Exercises which will ask to write Python commands in the following cells.

Shortcut keys:

  • to execute Python code inside a Jupyter cell, press Control + Enter

  • to execute Python code inside a Jupyter cell AND select next cell, press Shift + Enter

  • to execute Python code inside a Jupyter cell AND a create a new cell aftwerwards, press Alt + Enter

  • If the notebooks look stuck, try to select Kernel -> Restart

Length of a list#

A list is a sequence, and like any sequence you can use the function len to obtain the length:

[1]:
a = [7,5,8]
[2]:
len(a)
[2]:
3
[3]:
b = [8,3,6,4,7]
[4]:
len(b)
[4]:
5

If a list contains other lists, they count as single elements:

[5]:
mixed = [
            [4,5,1],
            [8,6],
            [7,6,0,8],
        ]
[6]:
len(mixed)
[6]:
3

WARNING: YOU CAN’T use len as a method

[3,4,2].len()  # WRONG

EXERCISE: Try writing [3,4,2].len() here, which error appears?

[7]:
# write here

#[3,4,2].len()

EXERCISE: Try writing [3,4,2].len WITHOUT the round parenthesis at the end, which error appears?

[144]:
# write here

#[3,4,2].len
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[144], line 3
      1 # write here
      2
----> 3 [3,4,2].len

AttributeError: 'list' object has no attribute 'len'

QUESTION: If x is some list, by writing:

len(len(x))

what do we get?

  1. the length of the list

  2. an error

  3. something else

ANSWER: 2: len wants a sequence as argument and gives back a number, so the internal call to len(x) produces a number which is given to the external len and at that point Python will complain it received a number instead of a sequence. Verify which error appears by writing len(len(x)) down here.

[9]:
# write code here

QUESTION: Look at this expression, without executing it. What does it produce?

[len([]), len([len(['a','b'])])]
  1. an error (which one?)

  2. a number (which one?)

  3. a list (which one?)

Try writing the result by hand, and then compare it with the one obtained by executing the code in a cell.

ANSWER: 3: the list [0, 1]

QUESTION: Look at this expression, without executing it. What does it produce?

len([[[],[]],[],[[[]]],[[],[]]])
  1. an error (which one?)

  2. a number (which one?)

  3. a list (which one?)

ANSWER: 2. produces the number 4

QUESTION: What does the following expression produce?

[[((len('ababb')))],len(["argg",('b'),("c")]), len([len("bc")])]

ANSWER: [[5], 3, 1]

Reading an element#

Like for strings, we can access an element a list element by putting the index of the position we want to access among square brackets:

[10]:
    # 0   1   2   3
la = [70, 60, 90, 50]

As for any sequence, the positions start from 0:

[11]:
la[0]
[11]:
70
[12]:
la[1]
[12]:
60
[13]:
la[2]
[13]:
90
[14]:
la[3]
[14]:
50

Like for any string, if we exaggerate with the index we get an error:

la[4]

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-134-09bfed834fa2> in <module>
----> 1 la[4]

IndexError: list index out of range

As in strings, we can obtain last element by using a negative index:

[15]:
    # 0   1   2   3
la = [70, 60, 90, 50]
[16]:
la[-1]
[16]:
50
[17]:
la[-2]
[17]:
90
[18]:
la[-3]
[18]:
60
[19]:
la[-4]
[19]:
70

If we go beyond the list length, we get an error:

la[-5]

---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-169-f77280923dce> in <module>
----> 1 la[-5]

IndexError: list index out of range

QUESTION: if x is some list, by writing:

x[0]

what do we get?

  1. the first element of the list

  2. always an error

  3. sometimes an element, sometimes an error according to the list

ANSWER: 3: if the list is empty Python will not find the element and will give us an error. Which one? Try writing in the cell down here [][0] and see what happens.

[20]:
# write code here

QUESTION: if x is some list, by writing:

x[len(x)]

what do we get?

  1. an element of the list

  2. always an error

  3. sometimes an element, sometimes an error according to the list

ANSWER: 2. always an error: len(x) will always be a number equal to the last available index + 1

Exercise - Gutenberg apprentice#

Such honor! So young and you have been hired as master Gutenberg apprentice! Your job is to compose the pages with the characters made with iron blocks, so your collaborators can then send everything to the printing press.

You have a chars list in which the original blocks are saved. Can you print the writing Gutenberg?

  • DO NOT write characters nor additional strings (so no 'g' nor 'G'!)

  • every character MAY be reused more than once

[ ]:
chars = ['b', 'e', 'g', 'n', 'r', 't', 'u']   # Gutenberg
#chars = ['a', 'm', 's', 'p', 'o', 'a', 't']  # Stampamos

l = chars      # Let's create a new handy variable

# write here

Show solution

[ ]:
chars = ['b', 'e', 'g', 'n', 'r', 't', 'u']   # Gutenberg
#chars = ['a', 'm', 's', 'p', 'o', 'a', 't']  # Stampamos

l = chars      # Let's create a new handy variable

# first solution: not optimal
print(l[2].upper(), l[6], l[5], l[1], l[3], l[0], l[1],  l[4], l[2])
# second solution:
print(f"{l[2].upper()}{l[6]}{l[5]}{l[1]}{l[3]}{l[0]}{l[1]}{l[4]}{l[2]}")
# third solution:
print("%s%s%s%s%s%s%s%s%s" % (l[2].upper(), l[6], l[5], l[1], l[3], l[0], l[1], l[4], l[2]))

Writing an element#

Since all the lists are MUTABLE, given a list object we can change the content of any cell inside.

For example, suppose you want to change the cell at index 2 of the list la, from 6 to 5:

[22]:
     #0  1  2  3
la = [7, 9, 6, 8]

We might write like this:

[23]:
la[2] = 5

[24]:
la
[24]:
[7, 9, 5, 8]

Let’s see what’s happening with Python Tutor:

[25]:
# WARNING: FOR PYTHON TUTOR TO WORK, REMEMBER TO EXECUTE THIS CELL with Shift+Enter
#          (it's sufficient to execute it only once)


import sys
sys.path.append('../')
import jupman
[26]:
#     0  1  2  3
la = [7, 9, 6, 8]
la[2] = 5

jupman.pytut()
[26]:
Python Tutor visualization

As you see, no new memory regions are created, it just overwrites an existing cell.

Exercise - a jammed parking lot#

You are the administrator of the condominum “The Pythonic Joy”. Every apartment has one or two parking spaces assigned, and each one is numbered from 1 to 11.

What follows is the current parking lot, and as you can see there are three spaces not assigned, because the flats 3, 4 and 7 have no tenants anymore:

[27]:
parking_lot = ["Carlo", "Apt.3", "Ernesto", "Apt.4", "Apt.7", "Pam", "Giovanna", "Camilla", "Giorgia", "Jessica", "Jim"]

To keep the order you decide to compact the assignments and leave the empty spaces at the far end (could be handy for parking the movers!)

Write some code to MODIFY parking_lot so to have:

>>> print(parking_lot)
['Carlo', 'Jessica', 'Ernesto', 'Jim', 'Giorgia', 'Pam', 'Giovanna', 'Camilla', 'App.7', 'App.3', 'App.4']
  • DO NOT create new lists (no [a,b, ...] nor list(a,b,...))

  • DO NOT write tenants nor apartment names (so no 'Jessica' nor 'Apt.3')

  • parking_lot may have variable length

  • assume the unassigned places are always 3 and in fixed position

parking-jam

[ ]:
parking_lot = ["Carlo", "Apt.3", "Ernesto", "Apt.4", "Apt.7", "Pam", "Giovanna", "Camilla", "Giorgia", "Jessica", "Jim"]
#result:      ['Carlo', 'Jessica', 'Ernesto', 'Jim', 'Giorgia', 'Pam', 'Giovanna', 'Camilla', 'Apt.7', 'Apt.3', 'Apt.4']
#parking_lot = ["Cristian", "Apt.3", "Edgar", "Apt.4", "Apt.7", "Pamela", "Giusy", "Cristina", "John"]
#result:       ['Cristian', 'Cristina', 'Edgar', 'John', 'Giusy', 'Pamela', 'Apt.7', 'Apt.3', 'Apt.4']

# write here
print(parking_lot)

Show solution

[ ]:
parking_lot = ["Carlo", "Apt.3", "Ernesto", "Apt.4", "Apt.7", "Pam", "Giovanna", "Camilla", "Giorgia", "Jessica", "Jim"]
#result:      ['Carlo', 'Jessica', 'Ernesto', 'Jim', 'Giorgia', 'Pam', 'Giovanna', 'Camilla', 'Apt.7', 'Apt.3', 'Apt.4']
#parking_lot = ["Cristian", "Apt.3", "Edgar", "Apt.4", "Apt.7", "Pamela", "Giusy", "Cristina", "John"]
#result:       ['Cristian', 'Cristina', 'Edgar', 'John', 'Giusy', 'Pamela', 'Apt.7', 'Apt.3', 'Apt.4']

parking_lot[1],parking_lot[-2] = parking_lot[-2],parking_lot[1]
parking_lot[3],parking_lot[-1] = parking_lot[-1],parking_lot[3]
parking_lot[4],parking_lot[-3] = parking_lot[-3],parking_lot[4]
print(parking_lot)

Mutating shared lists#

WARNING: READ VERY WELL !!!

90% OF PROGRAMMING ERRORS ARE CAUSED BY MISUNDERSTANDING THIS TOPIC !!!

What happens when we associate the same identical mutable object to two variables, like for example a list, and then we mutate the object using one of the two variables?

Let’s look at an example - first, we associate the list [7,9,6] to variable la:

[29]:
la = [7,9,6]

Now we define a new variable lb, and we associate the same value that was already associated to variable la. Note: we are NOT creating new lists !

[30]:
lb = la
[31]:
print(la)  # la is always the same
[7, 9, 6]
[32]:
print(lb)  # lb is the *same* list associated to la
[7, 9, 6]

We can now try modifying a cell of lb, putting 5 in the cell at index 0:

[33]:
lb[0] = 5

If we try printing the variables la and lb, Python will look at the values associated to each variable. Since the value is the same identical list (which is in the same identical memory region), in both cases you will see the change we just did !

[34]:
print(la)
[5, 9, 6]
[35]:
print(lb)
[5, 9, 6]

Let’s see in detail what happens with Python Tutor:

[36]:
la = [7,9,6]
lb = la
lb[0] = 5
print('la is', la)
print('lb is', lb)

jupman.pytut()
la is [5, 9, 6]
lb is [5, 9, 6]
[36]:
Python Tutor visualization

Let’s see the difference when we explicitly create a list equal to la.

In this case we will have two distinct memory regions and la will NOT be modified:

[37]:
la = [7,9,6]
lb = [7,9,6]
lb[0] = 5
print('la is', la)
print('lb is', lb)

jupman.pytut()
la is [7, 9, 6]
lb is [5, 9, 6]
[37]:
Python Tutor visualization

QUESTION: After executing this code, what will be printed? How many lists will be present in memory?

Try drawing ON PAPER what is supposed to happen in memory, and then compare with Python Tutor!

la = [8,7,7]
lb = [9,6,7,5]
lc = lb
la = lb
print('la is', la)
print('lb is', lb)
print('lc is', lc)

ANSWER: it will print:

la is [9, 6, 7, 5]
lb is [9, 6, 7, 5]
lc is [9, 6, 7, 5]

because

la = [8,7,7]
lb = [9,6,7,5]

lc = lb   # variable lc is associated to the same identical list of lb

la = lb   # variable la is associated to the same identical list of lb
          # the list previously associated to la is lost
[38]:
la = [8,7,7]
lb = [9,6,7,5]
lc = lb
la = lb
#print('la is', la)
#print('lb is', lb)
#print('lc is', lc)
jupman.pytut()
[38]:
Python Tutor visualization

QUESTION: Look at the following code. After its execution, by printing la, lb and lc what will we get?

Try drawing ON PAPER what is happening in memory, then compare the result with Python Tutor!

la = [7,8,5]
lb = [6,7]
lc = lb
lb = la
lc[0] = 9
print('la is', la)
print('lb is', lb)
print('lc is', lc)

ANSWER: The print will produce

la is [7, 8, 5]
lb is [7, 8, 5]
lc is [9, 7]

because :

la = [7,8,5]
lb = [6,7]
# the variable lc is assigned to the same list of lb [6,7]
lc = lb
# the variable lb is associated to the same list of la [7,8,5].
# This doesn't change the assignment of lc, which remains associated to [6,7] !
lb = la
# Modifies the first element of the list associated to lc which from [6,7] becomes [9,7]
lc[0] = 9
print('la is', la)
print('lb is', lb)
print('lc is', lc)
[39]:
la = [7,8,5]
lb = [6,7]
lc = lb
lb = la
lc[0] = 9
#print('la is', la)
#print('lb is', lb)
#print('lc is', lc)

jupman.pytut()
[39]:
Python Tutor visualization

List of strings#

We said we can put any object into a list, for example some strings:

[40]:
vegetables = ['tomatoes', 'onions', 'carrots', 'cabbage']

Let’s try extracting a vegetable by writing this expression:

[41]:
vegetables[2]
[41]:
'carrots'

Now, the preceding expression produces the result 'carrots', which we know is a string. This suggests we can use the expression exactly like if it were a string.

Suppose we want to obtain the first character of the string 'carrots', if we directly have the string we can write like this:

[42]:
'carrots'[0]
[42]:
'c'

But if the string is inside the previous list, we could directly do like this:

[43]:
vegetables[2][0]
[43]:
'c'

Exercise - province codes#

Given a list with exactly 4 province codes in lowercase, write some code which creates a NEW list containing the same codes in uppercase characters.

  • your code must work with any list of 4 provinces

  • hint: if you don’t remember the right method, have a look here

Example 1 - given:

provinces = ['tn','mi','to','ro']

your code must print:

['TN', 'MI', 'TO', 'RO']

Example 2 - given:

provinces = ['pa','ge','ve', 'aq']

Your code must print:

['PA', 'GE', 'VE', 'AQ']

plate

[ ]:
provinces = ['tn','mi','to','ro']
#provinces = ['pa','ge','ve', 'aq']

# write here

Show solution

[ ]:
provinces = ['tn','mi','to','ro']
#provinces = ['pa','ge','ve', 'aq']

print([provinces[0].upper(), provinces[1].upper(), provinces[2].upper(), provinces[3].upper()])

Exercise - games#

Given a list games of exactly 3 strings, write some code which MODIFIES the list so it contains only the first characters of each string.

  • Your code must work with any list of exactly 3 strings

Example - given:

games = ["Monopoly","RISK","Bingo"]

After executing the code, it must result:

>>> print(games)
["M","R","B"]
[ ]:
games = ["Monopoly", "RISK", "Bingo"]  # ['M','R','T']
#games = ["Frustration", "Game of the Goose", "Scrabble"]   # ['F','G','S']

# write here
print(games)

Show solution

[ ]:
games = ["Monopoly", "RISK", "Bingo"]  # ['M','R','T']
#games = ["Frustration", "Game of the Goose", "Scrabble"]   # ['F','G','S']

games = [ games[0][0], games[1][0], games[2][0] ]
print(games)

Slices#

We can extract sequences from lists by using slices. A slice is produced by placing square brackets after the list with inside the starting index (INCLUDED), followed by a colon :, followed by the end index (EXCLUDED). It works exactly as with strings: in that case the slice produces a new string, in this case it produces a NEW list. Let’s see an example:

[46]:
     #0  1  2  3  4  5  6  7  8  9
la = [40,30,90,80,60,10,40,20,50,60]
[47]:
la[3:7]
[47]:
[80, 60, 10, 40]

We extracted a NEW list [80, 60, 10, 40] from the list la starting from index 3 INCLUDED until index 7 EXCLUDED. We can see the original list is preserved:

[48]:
la
[48]:
[40, 30, 90, 80, 60, 10, 40, 20, 50, 60]

Let’s verify what happens with Python Tutor, by assigning the new list to a variable lb:

[49]:
#     0  1  2  3  4  5  6  7  8  9
la = [40,30,90,80,60,10,40,20,50,60]
lb = la[3:7]

jupman.pytut()
[49]:
Python Tutor visualization

You will notice a NEW memory region, associated to variable lb.

Slice - limits#

When we operate with slices we must be careful about indeces limits. Let’s see how they behave:

[50]:
#0  1  2  3  4
[50,90,70,80,60][0:3]  # from index 0 *included* to 3 *excluded*
[50]:
[50, 90, 70]
[51]:
#0  1  2  3  4
[50,90,70,80,60][0:4]  # from index 0 *included* a 4 *excluded*
[51]:
[50, 90, 70, 80]
[52]:
#0  1  2  3  4
[50,90,70,80,60][0:5]  # from index 0 *included* to 5 *excluded*
[52]:
[50, 90, 70, 80, 60]
[53]:
#0  1  2  3  4
[50,90,70,80,60][0:6]   # if we go beyond the list length Python does not complain
[53]:
[50, 90, 70, 80, 60]
[54]:
#0  1  2  3  4
[50,90,70,80,60][8:12] # Python doesn't complain even if we start from non-existing indeces
[54]:
[]

QUESTION: This expression:

[][3:8]
  1. produces a result (which one?)

  2. produces an error (which one?)

ANSWER: given an empty list, we are trying yo create a sublist which goes from index 3 INCLUDED to index 8 EXCLUDED. As we’ve seen before, if we start after the limit and also if we go beyond the limit Python does not complain, and when elements are not found we are simply served with an empty list.

QUESTION: if x is some list (may also empty), what does this expression do? Can it give an error? Does it return something useful?

x[0:len(x)]

ANSWER: Always return a NEW copy of the entire list, because it starts from index 0 INCLUDED and ends at index len(x) EXCLUDED.

It also works with the empty list, because [][0:len([])] is equivalent to [][0:0] that is sublist from 0 included to 0 excluded, so we are not taking any character and are not going beyod list limits. In fact, as we’ve seen before, even if we went beyond Python wouldn’t complain.

Exercise - The ‘treccia mochena’#

As you well know, a wonderful pastry is made in the Mocheni valley in Trentino: the famous ‘treccia mochena’.

At a quick glance, it may look like a braid loaf, between 30 and 60 cm long with inside a mix of ingredients along a marvellous and secret cream.

With your friends Camilla and Giorgio, you bought a treccia divided in a certain number of portions stuffed with walnuts, bluberries and red currants.

treccia = ['w', 'w', 'w', 'w', 'w', 'b', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c']
walnuts,bluberries,currants  = 5,6,4

You like the bluberries, Giorgio likes walnuts and Camilla the red currants.

Write some code to place into variables mine, giorgio and camilla some lists obtained from treccia, and PRINT the result:

   Mine: ['b', 'b', 'b', 'b', 'b', 'b']
Giorgio: ['w', 'w', 'w', 'w', 'w']
Camilla: ['c', 'c', 'c', 'c']
  • suppose treccia has always only 3 ingredients

  • DO NOT write constant numbers (except 0)

[ ]:
#                                             Walnuts                  Bluberries                    Currants
walnuts,bluberries,currants,treccia  = 5,6,4,['w', 'w', 'w', 'w', 'w', 'b', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c']
#walnuts,bluberries,currants,treccia = 2,4,3,['W', 'W', 'B', 'B', 'B', 'B', 'C', 'C', 'C']

# write here

Show solution

[ ]:
#                                             Walnuts                  Bluberries                    Currants
walnuts,bluberries,currants,treccia  = 5,6,4,['w', 'w', 'w', 'w', 'w', 'b', 'b', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c']
#walnuts,bluberries,currants,treccia = 2,4,3,['W', 'W', 'B', 'B', 'B', 'B', 'C', 'C', 'C']

mine = treccia[walnuts : walnuts+bluberries]
giorgio = treccia[0 : walnuts]
camilla = treccia[walnuts+bluberries : walnuts+bluberries+currants]
print("   Mine: %s \nGiorgio: %s \nCamilla: %s" % (mine, giorgio, camilla))

Slices - omitting limits#

If we will, it’s possible to omit start index, in which case Python will suppose it’s 0:

[56]:
#0  1  2  3  4  5  6  7  8  9
[90,60,80,70,60,90,60,50,70][:3]
[56]:
[90, 60, 80]

It’s also possible to omit the end index, in this case Python will extract elements until the list end:

[57]:
#0  1  2  3  4  5  6  7  8  9
[90,60,80,70,60,90,60,50,70][3:]
[57]:
[70, 60, 90, 60, 50, 70]

By omitting both indexes we obtain the full list:

[58]:
#0  1  2  3  4  5  6  7  8  9
[90,60,80,70,60,90,60,50,70][:]
[58]:
[90, 60, 80, 70, 60, 90, 60, 50, 70]

QUESTION: What is this code going to print? Will la get modified or not?

la = [7,8,9]
lb = la[:]
lb[0] = 6
print('la =',la)
print('lb =',lb)

ANSWER: lb = la[:] creates a NEW list containing all the elements which are in la. When we write lb[0] = 6 we are only modifying the memory region associated to lb. If you observe it in Python Tutor, you will see that la and lb are pointing to different memory regions:

[59]:
la = [7,8,9]
lb = la[:]
lb[0] = 6
#print('la =',la)
#print('lb =',lb)

jupman.pytut()
[59]:
Python Tutor visualization

QUESTION: For each of the following expressions, try guessing which value it produces, or if it gives an error.

  1. python         [9,7,8,6][1:1]

  2. python         [9,7,8,6][1:2]

  3. python         [9,7,8,6][2:3][0]

  4. python     [][]

  5. python     [][:]

  6. python     [3][:]

  7. python         [:][]

Exercise - An out of tune guitar#

In the attic you found an old guitar which was around when you were a child. Now that you have a degree in Sound Engineering you try playing it while a sensor measures the notes it produces.

The sensor is a microphone, and each one tenth of second it records the main note it detected.

You discover this phenomena: when played, some guitar strings have an oscillating behaviour, but then they synthonize on a precise note until the end:

'D', 'A', 'F', 'E', 'B', 'G', 'B', 'F', 'F', 'F', 'F', 'F', 'F', 'F', '...'

Other strings do the opposite:

'C', 'C', 'C', 'C', 'C', 'D', 'G', 'F', 'B', 'B', 'C', 'B', '...'

A list cutoffs records for each string the instants in which the weird behaviour starts or ends. The instants are represented as a sequence of beats '*', for example the first string starts an anomalous behaviour after 5 beats: '*****', while the seconds ends the anomalous behaviour after 7 beats: '*******'

Write some code to cut the sensor output and obtain only the sequences of continuous and correct notes. In the end, it should PRINT:

['C', 'C', 'C', 'C', 'C']
['F', 'F', 'F', 'F', 'F', 'F', 'F', '...']
['D', 'D', 'D', 'D', 'D', 'D', '...']
['G', 'G', 'G', 'G']
  • DO NOT write constant numbers of instants in the code, instead, use the variable cutoffs

[ ]:
cutoffs = ['*****',
           '*******',
           '***',
           '****',]

string1 = ['C', 'C', 'C', 'C', 'C', 'D', 'G', 'F', 'B', 'B', 'C', 'B', '...']
string2 = ['D', 'A', 'F', 'E', 'B', 'G', 'B', 'F', 'F', 'F', 'F', 'F', 'F', 'F', '...']
string3 = ['B', 'E', 'G', 'D', 'D', 'D', 'D', 'D', 'D', '...']
string4 = ['G', 'G', 'G', 'G', 'D', 'D', 'A', 'A', 'B', 'F', 'B', '...']

# write here

Show solution

[ ]:
cutoffs = ['*****',
           '*******',
           '***',
           '****',]

string1 = ['C', 'C', 'C', 'C', 'C', 'D', 'G', 'F', 'B', 'B', 'C', 'B', '...']
string2 = ['D', 'A', 'F', 'E', 'B', 'G', 'B', 'F', 'F', 'F', 'F', 'F', 'F', 'F', '...']
string3 = ['B', 'E', 'G', 'D', 'D', 'D', 'D', 'D', 'D', '...']
string4 = ['G', 'G', 'G', 'G', 'D', 'D', 'A', 'A', 'B', 'F', 'B', '...']

string1_clean = string1[:len(cutoffs[0])]
string2_clean = string2[len(cutoffs[1]):]
string3_clean = string3[len(cutoffs[2]):]
string4_clean = string4[:len(cutoffs[3])]

print(string1_clean)
print(string2_clean)
print(string3_clean)
print(string4_clean)

Slices - negative limits#

It’s also possible to set inverse and negative limits, although it’s not always intuitive:

[61]:
#0  1  2  3  4  5  6
[70,40,10,50,60,10,90][3:0]   # from index 3 to positive indexes <= 3 produces nothing
[61]:
[]
[62]:
#0  1  2  3  4  5  6
[70,40,10,50,60,10,90][3:1]  # from index 3 to positive indexes <= 3 produces nothing
[62]:
[]
[63]:
#0  1  2  3  4  5  6
[70,40,10,50,60,10,90][3:2]   # from index 3 to positive indexes <= 3 produces nothing
[63]:
[]
[64]:
#0  1  2  3  4  5  6
[70,40,10,50,60,10,90][3:3]   # from index 3 to positive indexes <= 3 produces nothing
[64]:
[]

Let’s see what happens with negative indexes:

[65]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][3:-1]
[65]:
[50, 60, 10]
[66]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][3:-2]
[66]:
[50, 60]
[67]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][3:-3]
[67]:
[50]
[68]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][3:-4]
[68]:
[]
[69]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][3:-5]
[69]:
[]

It’s also possible to start from a negative index and arrive to a positive one. As long as the first index marks a position which precedes the second index, something gets returned:

[70]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][-7:3]
[70]:
[70, 40, 10]
[71]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][-6:3]
[71]:
[40, 10]
[72]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][-5:3]
[72]:
[10]
[73]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][-4:3]
[73]:
[]
[74]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][-3:3]
[74]:
[]
[75]:
# 0  1  2  3  4  5  6
#-7 -6 -5 -4 -3 -2 -1
[70,40,10,50,60,10,90][-2:3]
[75]:
[]

QUESTION: For each of the following expressions, try guessing which value is produced, or if it gives an error

  1. [9,7,8,6][0:-2]
    
  2. [0:-2][9,7,8,6]
    
  3. [5,7,9][1:-1]
    
  4. [][-13:-17]
    
  5. [9,7,8,6][-4:-1]
    
  6. [9,7,8,6][-5:-1]
    
  7. [9,7,8,6,10,32][-3:1]
    
  8. [9,7,8,6,10,32][-3:5]
    

Exercise - The NonsenShop#

To keep your expenses under control you want to write a software which tracks everything you buy, and reduce pointless expenses.

You just need to take a picture of the receit with the smartphone, and an OCR (Optical Character Recognition) module will automatically read the text.

  • all receits have the same schema: shop name, date, “Bought items”, 1 row per item, total, thanks.

  • assume all the receits are always ordered by price

  1. Write some code to create a NEW list with only the items rows, for example:

['Green varnish for salad        1,12€',
 'Anti-wind lead confetti        4,99€',
 'Comb for pythons               12,00€',
 'Cigarette lighter for diving   23,00€',
 'Transparent home shoes         35,56€']
  1. Print the most expensive item like this:

Most expensive item was: Transparent home shoes
                   cost: 35,56€
[ ]:
receit = ["NonsenShop",
          "July 21,  2021 14:54",
          "Items:",
          "Green varnish for salad        1,12€",
          "Anti-wind lead confetti        4,99€",
          "Comb for pythons               12,00€",
          "Cigarette lighter for diving   23,00€",
          "Transparent home shoes         35,56€",
          "Total                          56,66€",
          "Thanks for buying our nonsense!"]

#receit = ["Eleganz",
#          "January 3 2020 12:53",
#          "Items:",
#          "Wedge heels with acquarium and fishes        342,00€",
#          "'Elvis' suit                                 20.000,00€",
#          "Total 20.000,342€",
#          "And now unleash the party animal in you!"]


# write here

Show solution

[ ]:
receit = ["NonsenShop",
          "July 21,  2021 14:54",
          "Items:",
          "Green varnish for salad        1,12€",
          "Anti-wind lead confetti        4,99€",
          "Comb for pythons               12,00€",
          "Cigarette lighter for diving   23,00€",
          "Transparent home shoes         35,56€",
          "Total                          56,66€",
          "Thanks for buying our nonsense!"]

#receit = ["Eleganz",
#          "January 3 2020 12:53",
#          "Items:",
#          "Wedge heels with acquarium and fishes        342,00€",
#          "'Elvis' suit                                 20.000,00€",
#          "Total 20.000,342€",
#          "And now unleash the party animal in you!"]


items = receit[3:-2]
last = receit[-3]

from pprint import  pprint
pprint(items)
print()
print("Most expensive item was:", last[:-10])
print("                   cost:", last[-10:].lstrip())

Slice - step#

It’s also possible to specify a third parameter called ‘step’ to tell Python how many cells to skip at each read. For example, here we start from index 3 and arrive to index 9 excluded, skipping by 2:

[77]:
# 0 1  2  3  4  5  6  7  8  9
[ 0,10,20,30,40,50,60,70,80,90][3:9:2]
[77]:
[30, 50, 70]

All the sequence, skipping by 3:

[78]:
# 0 1  2  3  4  5  6  7  8  9
[ 0,10,20,30,40,50,60,70,80,90][0:10:3]
[78]:
[0, 30, 60, 90]

We can also omit the limits to obtain the equivalent expression:

[79]:
# 0 1  2  3  4  5  6  7  8  9
[ 0,10,20,30,40,50,60,70,80,90][::3]
[79]:
[0, 30, 60, 90]

Slices - modifying#

Suppose we have the list

[80]:
     #0  1  2  3  4  5  6  7
la = [30,40,80,10,70,60,40,20]

and we want to change la cells from index 3 INCLUDED to index 6 EXCLUDED in such a way they contain the numbers taken from list [91,92,93]. We can do it with this special notation which allows us to write a slice to the left of operator =:

[81]:
la[3:6] = [91,92,93]
[82]:
la
[82]:
[30, 40, 80, 91, 92, 93, 40, 20]

In this slightly more complex example we verify in Python Tutor that the original memory region gets actually modifyied:

[83]:
#     0  1  2  3  4  5  6  7
la = [30,40,80,10,70,60,40,20]
lb = la
lb[3:6] = [91,92,93]

jupman.pytut()
[83]:
Python Tutor visualization

QUESTION: Look at the following code - what does it produce?

la = [9,6,5,8,2]
la[1:4] = [4,7,0]
print(la)
  1. modify la (how?)

  2. an error (which one?)

ANSWER: 1 - MODIFIES la like this:

# 0  1  2  3  4
[ 9, 4, 7, 0, 2]

so from index 1 INCLUDED to index 4 EXCLUDED

QUESTION: Look at the following code. What does it produce?

la = [7,6,8,4,2,4,2,3,1]
i = 3
lb = la[0:i]
la[i:2*i] = lb
print(la)
  1. modifies la (how?)

  2. an error (which one?)

ANSWER: 1 - modifies la by copying first i cells into successive ones.

Exercise - The railway outlaws#

United States - May 13th, 1857

The colonization of the West is a hard job but somebody gotta do it. Mountains are stuffed with rare minerals and cute animals to be transformed into precious fur coats for noblewomans of Europe. As always, with great wealth come great outlaws.

You are the station master of Denver and you must manage the trains. There are three main lines, Colorado Springs, Fort Collins e New York:

  • from Colorado Springs always arrive exactly 1 wagon of coal, 3 of minerals and 3 of passengers alternated.

  • from Fort Collins always arrive exactly 2 wagons of coal, 2 of passengers and 2 of cattle

When trains reach Denver, their content is transfered into the empty wagons of the New York train like this:

  • to prevent robberies, all the precious wagons are to be positioned nearby the locomotive

  • the cattle is to be placed always behind passengers, because as much as hygiene in the west is lacking, it’s still easier to pretend humans bathe more than cattle

Write some code which MODIFIES the ORIGINAL memory region of new_york list by copying the strings from colorado and fort

  • MINIMIZE the number of assignments! (the best solution with just slices has only has three assignments!)

  • DO NOT write constant strings in your code (so no "cowboy", "gold", …)

Example - given:

[84]:
colorado = ["CS locomotive","coal", "cowboy",    "gold","miners", "gold","cowboy","silver"]
fort     = ["FC locomotive","coal", "coal",  "gentlmen","ladies", "cows","horses"]
new_york = ["NY locomotive","coal",       "",       "",       "",     "",      "",      "", "","",""]

after your code it must result:

>>> print(new_york)
['NY locomotive', 'coal', 'gold', 'gold', 'silver', 'cowboy', 'miners', 'cowboy', 'gentlmen', 'ladies', 'cows', 'horses']
[ ]:
           # 0               1       2        3          4         5      6        7        8  9  10
colorado = ["CS locomotive","coal", "cowboy",    "gold","miners", "gold","cowboy","silver"]
fort     = ["FC locomotive","coal", "coal",  "gentlmen","ladies", "cows","horses"]
new_york = ["NY locomotive","coal",       "",       "",       "",     "",      "",      "", "","",""]

# write here
print(new_york)

Show solution

[ ]:
           # 0               1       2        3          4         5      6        7        8  9  10
colorado = ["CS locomotive","coal", "cowboy",    "gold","miners", "gold","cowboy","silver"]
fort     = ["FC locomotive","coal", "coal",  "gentlmen","ladies", "cows","horses"]
new_york = ["NY locomotive","coal",       "",       "",       "",     "",      "",      "", "","",""]

new_york[2:5] = colorado[3::2]
new_york[5:]  = colorado[2::2]
new_york[8:]  = fort[3:]
print(new_york)
[86]:
           # 0               1       2        3          4         5      6        7        8  9  10
colorado = ["CS locomotive","coal", "cowboy",    "gold","miners", "gold","cowboy","silver"]
fort     = ["FC locomotive","coal", "coal",  "gentlmen","ladies", "cows","horses"]
new_york = ["NY locomotive","coal",       "",       "",       "",     "",      "",      "", "","",""]

# Note the '+' is only  presented in following sections and here would also be inefficient
new_york[2:8] = colorado[3::2] + colorado[2::2]
new_york[8:]  = fort[3:]
print(new_york)
['NY locomotive', 'coal', 'gold', 'gold', 'silver', 'cowboy', 'miners', 'cowboy', 'gentlmen', 'ladies', 'cows', 'horses']

List of lists#

NOTE: We will talk much more in detail of lists of lists in the tutorial Matrices - list of lists, this is just a brief introduction.

The consideration we’ve seen so far about string lists are also valid for a list of lists:

[87]:
couples = [             # external list
            [67,95],    #    internal list at index 0
            [60,59],    #                     index 1
            [86,75],    #                     index 2
            [96,90],    #                     index 3
            [88,87],    #                     index 4
          ]

If we want ot extract the number 90, we must first extract the sublist from index 3:

[88]:
couples[3]   # NOTE: the expression result is a list
[88]:
[96, 90]

and so in the extracted sublist (which has only two elements) we can recover the number at index 0:

[89]:
couples[3][0]
[89]:
96

and at index 1:

[90]:
couples[3][1]
[90]:
90

Exercise - couples#

  1. Write some code to extract and print the numbers 86, 67 and 87

  2. Given a row with index i and a column j, print the number at row i and column j multiplied by the number at successive row and same column

After your code, you should see printed

1) 86 67 87

2) i = 3  j = 1  result = 7830
[ ]:
couples = [             # external list
            [67,95],    # internal list at index 0
            [60,59],    # internal list at index 1
            [86,75],    # internal list at index 2
            [96,90],    # internal list at index 3
            [88,87],    # internal list at index 4
          ]

i = 3
j = 1

# write here

Show solution

[ ]:
couples = [             # external list
            [67,95],    # internal list at index 0
            [60,59],    # internal list at index 1
            [86,75],    # internal list at index 2
            [96,90],    # internal list at index 3
            [88,87],    # internal list at index 4
          ]

i = 3
j = 1

print("1)", couples[2][0], couples[0][0], couples[4][1])
print()
print("2)"," i =",i," j =",j," result =", couples[i][j]*couples[i+1][j])

Exercise - Glory to Gladiators!#

The gladiators fight for the glory of the battle and the entertainment of the Emperor and the people! Sadly, not all gladiators manage to fight the same number of battles..

For each fight, each gladiator receives a reward in sesterces (in case he doesn’t survive, it will be offered to his patron…)

At the end of the games, the Emperor throws the prize in sesterces to his favourite gladiator. The Emperor has bad aim and his weak arms don’t allow him to throw everything at once, so he always ends up throwing half of the money to the chosen gladiator and half to the next gladiator.

  • NOTE: the Emperor never chooses the last of the list

Given a prize and gladiators list of sublists of arbitrary length, and a gladiator at index i, write some code which MODIFIES the sublists of gladiators at row i and following one so to increase the last element of both lists of half prize.

  • Your code must work with any prize, gladiators and i

Example - given these gladiators:

gladiators = [
               [67,95],
               [60,23,23,13,59],
               [86,75],
               [96,90,92],
               [88,87],
             ]

and given:

prize, i = 40, 1

after your code, by writing (we use pprint so printing happens on many lines) it must result:

>>> from pprint import pprint
>>> pprint(gladiatori,width=30)
[[67, 95],
 [60, 23, 23, 13, 79],
 [86, 95],
 [96, 90, 92],
 [88, 87]]
[ ]:
prize, i = 40, 1                      # sesterces, gladiator to award
#prize, i = 10, 3

gladiators = [                   # external list
               [67,95],          # internal list at index 0
               [60,23,23,13,59], # internal list at index 1
               [86,75],          # internal list at index 2
               [96,90,92],       # internal list at index 3
               [88,87],          # internal list at index 4
             ]

# write here

Show solution

[ ]:
prize, i = 40, 1                      # sesterces, gladiator to award
#prize, i = 10, 3

gladiators = [                   # external list
               [67,95],          # internal list at index 0
               [60,23,23,13,59], # internal list at index 1
               [86,75],          # internal list at index 2
               [96,90,92],       # internal list at index 3
               [88,87],          # internal list at index 4
             ]

gladiators[i][-1]   += prize // 2
gladiators[i+1][-1] += prize // 2

from pprint import pprint
pprint(gladiators,width=30)

Membership#

To verify whether an object is contained in a list, we can use the in operator.

Note the result of this expression is a boolean:

[93]:
9 in [6,8,9,7]
[93]:
True
[94]:
5 in [6,8,9,7]
[94]:
False
[95]:
"apple" in ["watermelon","apple","banana"]
[95]:
True
[96]:
"carrot" in ["watermelon","apple","banana"]
[96]:
False

Do not abuse in#

WARNING: in is often used in a wrong / inefficient way

Always ask yourself:

  1. Could the list not contain the substring we’re looking for? Always remember to also handle his case!

  2. in performs a search on all the list, which might be inefficient: is it really necessary, or we already know the interval where to search?

  3. If we wanted to know whether element is in a position we know a priori (i.e. 3), in is not needed, it’s enough to write my_list[3] == element. By using in Python might find duplicated elements which are before or after the one we want to verify!

QUESTION: What’s the result of this expression? True or False?

True in [ 5 in [6,7,5],
          2 in [8,1]
        ]

ANSWER: Gives back True because

[ 5 in [6,7,5],
  2 in [8,1]
]

represents a list of two elements. Each element is an expression with in which gets evaluated. In the first case, 5 in [6,7,5] results True. So the final list becomes [True, False] and by writing True in [True, False] we obtain True

not in#

We can write the check of non belonging in two ways:

Way 1:

[97]:
"carrot" not in ["watermelon","banana","apple"]
[97]:
True
[98]:
"watermelon" not in ["watermelon","banana","apple"]
[98]:
False

Way 2:

[99]:
not "carrot" in ["watermelon","banana","apple"]
[99]:
True
[100]:
not "watermelon" in ["watermelon","banana","apple"]
[100]:
False

QUESTION: Given any element x and list y, what does the following expression produce?

x in y and not x in y
  1. False

  2. True

  3. False or True according to the values of x and y

  4. an error

ANSWER: 1. Gives back False, because internally Python brackets the expression like this:

(x in y) and (not x in y)

and one element cannot be both contained in the list and not contained in the same list

QUESTION: For each of the following expressions, try to guess the result

  1. 3 in [3]
    
  2. [4,5] in [1,2,3,4,5]
    
  3. [4,5] in [[1,2,3],[4,5]]
    
  4. [4,5] in [[1,2,3,4],[5,6]]
    
  5. 'n' in ['alien'[-1]]
    
  6. 'rts' in 'karts'[1:4]
    
  7. [] in [[[]]]
    
  8. python         [] in [[]]

  9. [] in ["[]"]
    

QUESTION: For each of the following expressions, independently from the value of x and y, tell whether it always results True:

  1. x in x
    
  2. x in [x]
    
  3. x not in []
    
  4. x in [[x]]
    
  5. x in [[x][0]]
    
  6. (x and y) in [x,y]
    
  7. x in [x,y] and y in [x,y]
    

Exercise - vegetables#

Given the list vegetables of exactly 5 strings and the list of strings fruits, MODIFY the variable vegetables so that in each cell there is True if the vegetable is a fruit or False otherwise.

  • your code must work with any list of 5 strings vegetables and any list fruits

Example - given:

vegetables = ["carrot",
              "cabbage",
              "apple",
              "aubergine",
              "watermelon"]

fruits = ["watermelon","banana","apple",]

after execution your code must print:

>>> print(vegetables)
[False, False, True, False, True]
[ ]:
vegetables = ["carrot",
              "cabbage",
              "apple",
              "aubergine",
              "watermelon"]

fruits = ["watermelon","banana","apple",]

# write here

print(vegetables)

Show solution

[ ]:
vegetables = ["carrot",
              "cabbage",
              "apple",
              "aubergine",
              "watermelon"]

fruits = ["watermelon","banana","apple",]

vegetables = [vegetables[0] in fruits,
              vegetables[1] in fruits,
              vegetables[2] in fruits,
              vegetables[3] in fruits,
              vegetables[4] in fruits,
]

print(vegetables)

List concatenation with +#

Given two lists la and lb, we can concatenate them with the operator + which produces a NEW list:

[102]:
la = [70,60,80]
lb = [90,50]

la + lb
[102]:
[70, 60, 80, 90, 50]

Note the operator + produces a NEW list, so la and lb remained unchanged:

[103]:
print(la)
[70, 60, 80]
[104]:
print(lb)
[90, 50]

Let’s check with Python Tutor:

[105]:
la = [70,60,80]
lb = [90,50]
lc = la + lb

print(la)
print(lb)
print(lc)

jupman.pytut()
[70, 60, 80]
[90, 50]
[70, 60, 80, 90, 50]
[105]:
Python Tutor visualization

Exercise - concatenation#

Write some code which given lists la and lb, puts into list lc the last two elements of la and the first two of lb

Example - given:

la = [18,26,30,45,55]
lb = [16,26,37,45]

after your code it must print:

>>> print(la)
[18, 26, 30, 45, 55]
>>> print(lb)
[16, 26, 37, 45]
>>> print(lc)
[45, 55, 16, 26]
[ ]:
la = [18,26,30,45,55]
lb = [16,26,37,45]

# write here

Show solution

[ ]:
la = [18,26,30,45,55]
lb = [16,26,37,45]

lc = la[-2:] + lb[:2]
print(la)
print(lb)
print(lc)

QUESTION: For each of the following expressions, try guessing the result

  1. [6,7,8] + [9]
    
  2. [6,7,8] + []
    
  3. [] + [6,7,8]
    
  4. [] + []
    
  5. [] + [[]]
    
  6. [[]]+[]
    
  7. [[]]+[[]]
    
  8. ([6] + [8])[0]
    
  9. ([6] + [8])[1]
    
  10. ([6] + [8])[2:]
    
  11. len([4,2,5])+len([3,1,2])
    
  12. len([4,2,5] + [3,1,2])
    
  13. [5,4,3] + "3,1"
    
  14. [5,4,3] + "[3,1]"
    
  15. "[5,4,3]" + "[3,1]"
    
  16. ["4","1","7"] + ["3","1"]
    
  17. list('coca') + ['c','o','l','a']
    

min and max#

A list is a sequence of elements, and as such we can pass it to functions min or max for finding respectively the minimum or the maximum element of the list.

[107]:
min([4,5,3,7,8,6])
[107]:
3
[108]:
max([4,5,3,7,8,6])
[108]:
8

V COMMANDMENT : You shall never ever use min and max as variable names

If you do, you will lose the functions!

Note it’s also possible to directly pass to min and max the elements to compare without including them in a list:

[109]:
min(4,5,3,7,8,6)
[109]:
3
[110]:
max(4,5,3,7,8,6)
[110]:
8

But if we pass only one, without including it in a list, we will get an error:

min(4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-156-bb3db472b52e> in <module>
----> 1 min(4)

TypeError: 'int' object is not iterable

The error tells us that when we pass only an argument, Python expects a sequence like a list:

[111]:
min([4])
[111]:
4

To min and max we can also pass strings, and we will get the character which is alphabetically lesser or greater:

[112]:
min("orchestra")
[112]:
'a'
[113]:
max("orchestra")
[113]:
't'

If we pass a list of strings, we will obtain the lesser or greater string in lexicographical order (i.e. the phonebook order)

[114]:
min(['the', 'sailor', 'walks', 'around', 'the', 'docks'])
[114]:
'around'
[115]:
max(['the', 'sailor', 'walks', 'around', 'the', 'docks'])
[115]:
'walks'

QUESTION: For each of the following expressions, try guessing the result (or if it gives an error)

  1. max(7)
    
  2. max([7])
    
  3. max([5,4,6,2])
    
  4. max([min([7,3])])
    
  5. max([])
    
  6. max(2,9,3)
    
  7. max([3,2,5] + [9,2,3])
    
  8. max(max([3,2,5], max([9,2,3]))
    
  9. max(min(3,6), min(8,2))
    
  10. min(max(3,6), max(8,2))
    
  11. max(['a','b','d','c'])
    
  12. max(['boat', 'dice', 'aloha', 'circle'])
    
  13. min(['void','','null','nada'])
    
  14. max(['hammer'[-1],'socket'[-1],'wrench'[-1]])
    
  15. min(['hammer'[-1],'socket'[-1],'wrench'[-1]])
    

sum#

With sum we can sum all the elements in a list:

[116]:
sum([1,2,3])
[116]:
6
[117]:
sum([1.0, 2.0, 0.14])
[117]:
3.14

WARNING: DO NOT use sum as variable name!

If you do, you will lose the function with very bad consequences!

QUESTION: For each of the following expressions, try guessing the result (or if it gives an error):

  1. sum[3,1,2]
    
  2. sum(1,2,3)
    
  3. la = [1,2,3]
    sum(la) > max(la)
    
  4. la = [1,2,3]
    sum(la) > max(la)*len(la)
    
  5. la = [4,2,6,4,7]
    lb = [max(la), min(la), max(la)]
    print(max(lb) != max(la))
    

Exercise - balance#

Given a list of n numbers balance with n even, write some code which prints True if the sum of all first n/2 numbers is equal to the sum of all successive ones.

  • your code must work for any number list

Example 1 - given:

balance = [4,3,7,1,5,8]

after your code, it must print:

True

Example 2 - given:

balance = [4,3,3,1,9,8]

after your code, it must print:

False
[ ]:
balance = [4,3,7,1,5,8]
#balance = [4,3,3,1,9,8]

# write here

Show solution

[ ]:
balance = [4,3,7,1,5,8]
#balance = [4,3,3,1,9,8]

n = len(balance)
sum(balance[:n//2]) == sum(balance[n//2:])

List replication#

To replicate the elements of a list, it’s possible to use the operator * which produces a NEW list:

[119]:
[7,6,8] * 2
[119]:
[7, 6, 8, 7, 6, 8]
[120]:
[7,6,8] * 3
[120]:
[7, 6, 8, 7, 6, 8, 7, 6, 8]

Note a NEW list is produced, and the original one is not modified:

[121]:
la = [7,6,8]
[122]:
lb = [7,6,8] * 3
[123]:
la   # original
[123]:
[7, 6, 8]
[124]:
lb   # expression result
[124]:
[7, 6, 8, 7, 6, 8, 7, 6, 8]

We can multiply a list of strings:

[125]:
la = ["a", "world", "of", "words"]
[126]:
lb = la * 2
[127]:
print(la)
['a', 'world', 'of', 'words']
[128]:
print(lb)
['a', 'world', 'of', 'words', 'a', 'world', 'of', 'words']

As long as we multiply lists which contain immutable elements like numbers or strings, no particular problems arise:

[129]:
la = ["a", "world", "of", "words"]
lb = la * 2

jupman.pytut()
[129]:
Python Tutor visualization

The matter becomes much more sophisticated when we multiply lists which contain mutable objects like other lists. Let’s see an example:

[130]:
la = [5,6]
lb = [7,8,9]
lc = [la,lb] * 2
[131]:
print(la)
[5, 6]
[132]:
print(lb)
[7, 8, 9]
[133]:
print(lc)
[[5, 6], [7, 8, 9], [5, 6], [7, 8, 9]]

By printing it, we see that the lists la and lb are represented inside lc - but how, exactly? print calls may trick you about the effective state of memory - to investigate further it’s convenient to use Python Tutor:

[134]:
la = [5,6]
lb = [7,8,9]
lc = [la,lb] * 2

jupman.pytut()
[134]:
Python Tutor visualization

Arggh ! A jungle of arrows will appear ! This happens because when we write [la, lb] we create a list with two references to other lists [5,6] and [7,8,9], and the operator * when duplicating it just copies references.

For now we stop here, we will see the implications details later in the tutorial matrices - lists of lists

Equality#

We can check whether two lists are equal with equality operator ==, which given two lists returns True if they contain equal elements or False otherwise:

[135]:
[4,3,6] == [4,3,6]
[135]:
True
[136]:
[4,3,6] == [4,3]
[136]:
False
[137]:
[4,3,6] == [4,3,6, 'ciao']
[137]:
False
[138]:
[4,3,6] == [2,2,8]
[138]:
False

We can check equality of lists with heterogenous elements:

[139]:
['apples', 3, ['cherries', 2], 6] == ['apples', 3, ['cherries', 2], 6]
[139]:
True
[140]:
['bananas', 3,['cherries', 2], 6] == ['apples', 3, ['cherries', 2], 6]
[140]:
False

To check for inequality, we can use the operatpr !=:

[141]:
[2,2,8] != [2,2,8]
[141]:
False
[142]:
[4,6,0] != [2,2,8]
[142]:
True
[143]:
[4,6,0] != [4,6,0,2]
[143]:
True

QUESTION: For each of the following expressions, guess whether it is True, False or it produces an error:

  1. [2,3,1] != [2,3,1]
    
  2. [4,8,12] == [2*2,4*2,6*2]
    
  3. [7,8][:] == [7,9-1]
    
  4. [7][0] == [[7]][0]
    
  5. [9] == [9][0]
    
  6. [max(7,9)] == [max([7]),max([9])]
    
  7. ['a','b','c'] == ['A','B','C']
    
  8. ['a','b'] != ['a','b','c']
    
  9. ["ciao"] != ["CIAO".lower()]
    
  10. [True in [True]] != [False]
    
  11. [][:] == []
    
  12. [[]] == [] + []
    
  13. [[],[]] == [] + []
    
  14. [[[]]] == [[[]+[]]]
    

Continue#

Go on with Lists 3 - Basic methods