Lists 2 - operators

Download exercises zip

Browse online files

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:

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

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

[6]:
mixed = [
            [4,5,1],
            [8,6],
            [7,6,0,8],
        ]
[7]:
len(mixed)
[7]:
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?

Show solution
[8]:
# write here


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

Show solution
[9]:
# write here


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

Show answer
[10]:
# 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.

Show answer

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?)

Show answer

QUESTION: What does the following expression produce?

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

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:

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

As for any sequence, the positions start from 0:

[12]:
la[0]
[12]:
70
[13]:
la[1]
[13]:
60
[14]:
la[2]
[14]:
90
[15]:
la[3]
[15]:
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:

[16]:
    # 0   1   2   3
la = [70, 60, 90, 50]
[17]:
la[-1]
[17]:
50
[18]:
la[-2]
[18]:
90
[19]:
la[-3]
[19]:
60
[20]:
la[-4]
[20]:
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

Show answer
[21]:
# 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

Show answer

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

Show solution
[22]:

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

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:

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

We might write like this:

[24]:
la[2] = 5

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

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

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


import jupman
[27]:
#     0  1  2  3
la = [7, 9, 6, 8]
la[2] = 5

jupman.pytut()
[27]:
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:

[28]:
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

Show solution
[29]:

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

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:

[30]:
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 !

[31]:
lb = la
[32]:
print(la)  # la is always the same
[7, 9, 6]
[33]:
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:

[34]:
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 !

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

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

[37]:
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]
[37]:
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:

[38]:
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]
[38]:
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)
Show answer
[39]:
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()
[39]:
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)
Show answer
[40]:
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()
[40]:
Python Tutor visualization

List of strings

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

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

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

[42]:
vegetables[2]
[42]:
'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:

[43]:
'carrots'[0]
[43]:
'c'

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

[44]:
vegetables[2][0]
[44]:
'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

Show solution
[45]:

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

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"]
Show solution
[46]:

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

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:

[47]:
     #0  1  2  3  4  5  6  7  8  9
la = [40,30,90,80,60,10,40,20,50,60]
[48]:
la[3:7]
[48]:
[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:

[49]:
la
[49]:
[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:

[50]:
#     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()
[50]:
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:

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

QUESTION: This expression:

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

  2. produces an error (which one?)

Show answer

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)]
Show answer

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)

Show solution
[56]:


# 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

Slices - omitting limits

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

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

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

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

By omitting both indexes we obtain the full list:

[59]:
#0  1  2  3  4  5  6  7  8  9
[90,60,80,70,60,90,60,50,70][:]
[59]:
[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)
Show answer
[60]:
la = [7,8,9]
lb = la[:]
lb[0] = 6
#print('la =',la)
#print('lb =',lb)

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

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

  1. [9,7,8,6][1:1]
    
  2. [9,7,8,6][1:2]
    
  3. [9,7,8,6][2:3][0]
    
  4. [][]
    
  5. [][:]
    
  6. [3][:]
    
  7. [:][]
    

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

Show solution
[61]:


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

Slices - negative limits

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

[62]:
#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
[62]:
[]
[63]:
#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
[63]:
[]
[64]:
#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
[64]:
[]
[65]:
#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
[65]:
[]

Let’s see what happens with negative indexes:

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

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:

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

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€
Show solution
[77]:

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

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:

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

All the sequence, skipping by 3:

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

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

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

Slices - modifying

Suppose we have the list

[81]:
     #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 =:

[82]:
la[3:6] = [91,92,93]
[83]:
la
[83]:
[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:

[84]:
#     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()
[84]:
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?)

Show answer

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?)

Show answer

Exercise - The railway outlaws

ferrovia

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:

[85]:
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']
Show solution
[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", "", "", "", "", "", "", "","",""] # write here

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:

[88]:
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:

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

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

[90]:
couples[3][0]
[90]:
96

and at index 1:

[91]:
couples[3][1]
[91]:
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
Show solution
[92]:

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

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:

gladiatori

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]]
Show solution
[93]:

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

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:

[94]:
9 in [6,8,9,7]
[94]:
True
[95]:
5 in [6,8,9,7]
[95]:
False
[96]:
"apple" in ["watermelon","apple","banana"]
[96]:
True
[97]:
"carrot" in ["watermelon","apple","banana"]
[97]:
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]
        ]
Show answer

not in

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

Way 1:

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

Way 2:

[100]:
not "carrot" in ["watermelon","banana","apple"]
[100]:
True
[101]:
not "watermelon" in ["watermelon","banana","apple"]
[101]:
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

Show answer

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. [] 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]
Show solution
[102]:

vegetables = ["carrot", "cabbage", "apple", "aubergine", "watermelon"] fruits = ["watermelon","banana","apple",] # write here

List concatenation with +

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

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

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

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

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

Let’s check with Python Tutor:

[106]:
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]
[106]:
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]
Show solution
[107]:

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

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.

[108]:
min([4,5,3,7,8,6])
[108]:
3
[109]:
max([4,5,3,7,8,6])
[109]:
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:

[110]:
min(4,5,3,7,8,6)
[110]:
3
[111]:
max(4,5,3,7,8,6)
[111]:
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:

[112]:
min([4])
[112]:
4

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

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

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

[115]:
min(['the', 'sailor', 'walks', 'around', 'the', 'docks'])
[115]:
'around'
[116]:
max(['the', 'sailor', 'walks', 'around', 'the', 'docks'])
[116]:
'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:

[117]:
sum([1,2,3])
[117]:
6
[118]:
sum([1.0, 2.0, 0.14])
[118]:
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
Show solution
[119]:

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

List replication

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

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

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

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

We can multiply a list of strings:

[126]:
la = ["a", "world", "of", "words"]
[127]:
lb = la * 2
[128]:
print(la)
['a', 'world', 'of', 'words']
[129]:
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:

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

jupman.pytut()
[130]:
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:

[131]:
la = [5,6]
lb = [7,8,9]
lc = [la,lb] * 2
[132]:
print(la)
[5, 6]
[133]:
print(lb)
[7, 8, 9]
[134]:
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:

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

jupman.pytut()
[135]:
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:

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

We can check equality of lists with heterogenous elements:

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

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

[142]:
[2,2,8] != [2,2,8]
[142]:
False
[143]:
[4,6,0] != [2,2,8]
[143]:
True
[144]:
[4,6,0] != [4,6,0,2]
[144]:
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

You can find more exercise in the notebook Lists 3 - basic methods

[ ]: