Lists 3 - Basic methods#

Download exercises zip#

Lists are objects of type list and have several methods for performing operations on them, let’s see the basic ones:

Method

Returns

Description

list.append(obj)

None

Adds a new element at the end of the list

list1.extend(list2)

None

Adds many elements at the end of the list

list.insert(int,obj)

None

Adds a new element into some given position

list.pop()

obj

Removes and return the element at last position

list.pop(int)

obj

Given an index, removes and return the element at that position

list.reverse()

None

Inverts the order of elements

list.sort()

None

Sorts the elements in-place

“sep”.join(seq)

string

produces a string concatenating all the elements in seq separated by "sep"

list.copy()

list

Copia superficialmente la list

The others are described at the page Search methods

WARNING 1: LIST METHODS MODIFY THE LIST ON WHICH ARE CALLED !

Whenever you call a method of a list (the object to the left of the dot .), you MODIFY the list itself (differently from string methods which always generate a new string without changing the original)

WARNING 2: LIST METHODS RETURN NOTHING!

They almost always return the object None (differently from strings which always return a new string)

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 lists3.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

append method#

We can MODIFY a list adding a single element at a time with the method append.

Suppose to start from an empty list:

[1]:
la = []

If we want to add as element the number 50, we can write like this:

[2]:
la.append(50)

Note the list we initalilly created got MODIFIED:

[3]:
la
[3]:
[50]

WARNING: la.append(50) returned NOTHING !!!!

Observe carefully the output of cell with instruction la.append(50), you will notice there is absolutely nothing. This happens because the purpose of append is to MODIFY the list on which it is called, NOT generating new lists.

We append another number at the end of the list:

[4]:
la.append(90)
[5]:
la
[5]:
[50, 90]
[6]:
la.append(70)
[7]:
la
[7]:
[50, 90, 70]

Let’s see what happened in Python Tutor:

[8]:
# 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
[9]:
la = []
la.append(50)
la.append(90)
la.append(70)

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

Note there is only one yellow memory region associated to variable la which gets expanded as you click on Next.

We said append method returns nothing, let’s try to add some detail. In the methods table, there is present a column named Returns. If you check it, for almost all methods included append there is indicated it returns None.

None is the most boring object in Python, because it literally means nothing. What can you do with nothing? Very few things, so few that whenever Jupyter finds as result the None object it doesn’t even print it. Try directly inserting None in a cell, you will see it won’t be reported in cell output:

[10]:
None

A way to force the print is by using the command print:

[11]:
print(None)
None

EXERCISE: What is the type of the object None? Discover it by using the funciton type

[88]:
type(None)
[88]:
NoneType

Let’s try repeating what happens with append. If you call the method append on a list, append silently MODIFIES the list, and RETURNS the object None as call result. Notice that Jupyter considers this object as non-interesting, so it doesn’t even print it.

Let’s try to get explicit about this misterious None. If it’s true that append produces it as call result, it means we can associate this result to some variable. Let’s try to associate it to variable x:

[13]:
la = []
x = la.append(70)

Now, if everything went as we wrote, append should have modified the list:

[14]:
la
[14]:
[70]

and there should be associated None to variable x. So, if we ask Jupyter to show the value associated to x and that value is None, nothing will appear:

[15]:
x

Note there is no output in the cell, apparently we are really in presence of a None. Let’s force the print:

[16]:
print(x)
None

Here it is! Probably you will be a little confused by all of this, so let’s check again what happens in Python Tutor:

[17]:
la = []
x = la.append(70)
print("la is", la)
print("x is", x)

jupman.pytut()
la is [70]
x is None
[17]:
Python Tutor visualization

What’s the final gist?

REUSING THE RESULT OF LIST METHODS CALLS IS ALMOST ALWAYS AN ERROR !

Since calling list methods returns None, which is a ‘useless’ object, trying to reuse it will almost surely produce an error

EXERCISE: Build a list by adding one element at a time with the method append. Add the elements 77, "test", [60,93] with three calls to append, and finally print the list.

After your code, you should see [77, 'test', [60, 93]]

[ ]:
la = []

# write here
print(la)

Show solution

[ ]:
la = []

la.append(77)
la.append("test")
la.append([60, 93])
print(la)

QUESTION: The following code:

la = []
la.append(80,70,90)
  1. produces an error (which one?)

  2. modifies the list (how?)

ANSWER: 1: append accepts only one argument, by passing more than one will produce an error, to see which try to execute the code in a cell.

QUESTION: The following code:

la = []
la.append(80).append(90)
  1. produces an error

  2. appends to la the numbers 80 and 90

ANSWER: 1: produces an error, because we said the call to la.append(80) MODIFIES the list la on which it is called and return the value None. If on None we try calling .append(90), since None is not a list we will get an error message. Make sure of this using Python Tutor.

QUESTION: let’s briefly go back to strings. Look at the following code (if you don’t remember what string methods do see here)

sa = '    trento    '
sb = sa.strip().capitalize()
print(sb)
  1. produces an error (which one?)

  2. changes sa (how?)

  3. prints something (what?)

ANSWER: 3: prints Trento. Differently from lists, the strings are immutable sequences: this means that when you call a method of strings you are sure it will RETURN a NEW string. So the first call to sa.strip() RETURNS the string without spaces at beginning and end of 'trento', and on this string the method capitalize() is called to make the first character uppercase.

If this is not clear to you, try to executing the following code in Python Tutor. It is equivalent to the one in the example but it explicitly shows the passage by assigning the result of calling sa.strip() to the extra variable x

sa = '    trento    '
x = sa.strip()
sb = x.capitalize()
print(sb)

QUESTION: Have a look at this code. Will it print something at the end? Or will it produce an error?

la = []
lb = []
la.append(lb)

lb.append(90)
lb.append(70)

print(la)

ANSWER: It will print[[90, 70]], because we put lb inside la.

Even if with first append we added lb as first element of la, aftwerwards it is perfectly lecit keeping on modifying lb by calling lb.append(90).

Try executing the code in Python Tutor, and see the arrows.

Exercise - augmenting a list 1#

Given the list la of fixed dimension 7, write some code to augment the empty list lb so to only contain the elements of la with even index (0, 2, 4, …).

  • Your code should work with any list la of fixed dimension 7

#   0 1 2 3 4 5 6
la=[8,4,3,5,7,3,5]
lb=[]

After your code, you should obtain:

>>> print(lb)
[8,3,7,5]
[ ]:
#   0 1 2 3 4 5 6
la=[8,4,3,5,7,3,5]
lb=[]

# write here

print(lb)

Show solution

[ ]:
#   0 1 2 3 4 5 6
la=[8,4,3,5,7,3,5]
lb=[]

lb.append(la[0])
lb.append(la[2])
lb.append(la[4])
lb.append(la[6])

print(lb)

extend method#

We’ve seen that with append we can augment a list one element at a time.

What if we wanted to add many elements in a single shot, maybe taken from another list?

We should use the method extend, which MODIFIES the list on which it is called by adding all the elements it finds in the input sequence.

[20]:
la = [70,30,50]
[21]:
lb = [40,90,30,80]
[22]:
la.extend(lb)
[23]:
la
[23]:
[70, 30, 50, 40, 90, 30, 80]
[24]:
lb
[24]:
[40, 90, 30, 80]

In the example above, extend is called on the variable la, and we passed lb as parameter

WARNING: la is MODIFIED, but the sequence we passed in round parenthesis is not (lb in the example)

QUESTION: the execution of method extend returns something? What do you see in the output of cell la.extend(lb) ?

ANSWER: extend, as all list methods, doesn’t return anything. To be more explicit, it returns the object None, which is not even printed by Jupyter.

Let’s verify what happened with Python Tutor:

[25]:
la = [70,30,50]
lb = [40,90,30,80]
la.extend(lb)

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

QUESTION: Look inside this code. Which will be the values associated to variables la ,lb and x after its execution?

la = [30,70,50]
lb = [80,40]
x = la.extend(lb)

print('la is ', la)
print('lb is ', lb)
print('x  is ', x)

ANSWER: It will print this:

la is  [30, 70, 50, 80, 40]
lb is  [80, 40]
x  is  None

la was MODIFIED by adding all the elements of lb.

The call to extend, like all list methods, returned the object None which was associated to variable x. Try to understand well what happend by using Python Tutor.

Extending with sequences#

We said that extend can take any generic sequence in the round parenthesis, not only lists. This means we can also try to pass a string. For example:

[26]:
la = [70,60,80]

s = "hello"

la.extend(s)
[27]:
la
[27]:
[70, 60, 80, 'h', 'e', 'l', 'l', 'o']

Since the string is a character sequence, extend took each of these elements and added them to la

QUESTION: was the value associated to variable s modified?

ANSWER: absolutely impossible, because a) extend only modifies the list on which it is called and b) strings are immutable anyway.

QUESTION: The following code:

la = [60,50]
la.extend(70,90,80)
  1. produces un error (which one?)

  2. modifies la (how?)

ANSWER: 1: produces an error, because we have to pass a SINGLE parameter to extend, which must be a sequence. Here instead we are passing many parameters. An alternative might be to build a list like this:

la = [60,50]
la.extend([70,90,80])

QUESTION: If this code is executed, what happens?

sa = "hello"
sb = "world"
sa.extend(sb)
  1. sa is modified (how?)

  2. we get an error (which one?)

ANSWER: 2: we obtain an error, because extend is an exclusive method of lists. It only belongs to lists because MODIFIES the object on which it is called - since strings are immutable objects, it wouldn’t make sense to change them.

QUESTION: If this code is executed, what happens?

la = [1,2,3]
lb = [4,5]
lc = [6,7,8]

la.extend(lb).extend(lc)
  1. la becomes [1,2,3,4,5,6,7,8]

  2. an error (which one?)

  3. la becomes [1,2,3,4,5] and an error (which one?)

QUESTION: 3: la becomes [1,2,3,4,5] and right after we get an error, because the call to la.extend(lb) MODIFIES la to [1,2,3,4,5] and RETURN the value None. At that point, Python tries to call the method extend on the object None, but since it is not a list, we get an error (to convince yourself, verify everything with Python Tutor !!!)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-45-0a08a154ada4> in <module>
      3 lc = [6,7,8]
      4
----> 5 la.extend(lb).extend(lc)

AttributeError: 'NoneType' object has no attribute 'extend'

Exercise: augmenting a list 2#

Given two lists la and lb and an element x, write some code to MODIFY la so that la contains at the end the element x followed by all other elements of lb

  • NOTE 1: your code should work with any la and lb

  • NOTE 2: id is a Python function which associates to each memory region a unique identifier. If you try printing id(la) before modyfing la and id(la) afterwards, you should obtain exactly the same id. If you obtain a different one, it means you generated an entirely new list. In that case, verify how it’s working with Python Tutor.

la = [5,9,2,4]
lb = [7,1,3]
x = 8

You should obtain:

>>> print(la)
[5,9,2,4,8,7,1,3]
>>> print(lb)
[7,1,3]
>>> print(x)
8
[ ]:
la = [5,9,2,4]
lb = [7,1,3]
x = 8

# write here

Show solution

[ ]:
la = [5,9,2,4]
lb = [7,1,3]
x = 8

la.append(x)
la.extend(lb)
print(la)
print(lb)
print(x)

Exercise - zslice#

Write some code which given two lists la (of at least 3 elements) and lb, MODIFIES lb in such a way to add 3 elements of la followed by the last 3 of la.

  • your code must work with any list

  • use extends and slices

la = ['a','b','c','d','e','f','g','h','i','l','m','n','o']
lb = ['z']

You should obtain:

>>> print(la)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'l', 'm', 'n', 'o']
>>> print(lb)
['z', 'a', 'b', 'c', 'm', 'n', 'o']
[ ]:
la = ['a','b','c','d','e','f','g','h','i','l','m','n','o']
lb = ['z']

# write here

print(la)
print(lb)

Show solution

[ ]:
la = ['a','b','c','d','e','f','g','h','i','l','m','n','o']
lb = ['z']


lb.extend(la[:3])  # a slice generates a list
lb.extend(la[-3:])

print(la)
print(lb)

Exercise - Zebarerun#

Write some code which given a list of three strings words and an empty list la, fills la with all the first 3 characters of every string in words.

  • your code must work with any list of 3 strings

  • use slices

Example given:

words = ["Zebras", "are", "running"]
la = []

Your code must show:

>>> print(t)
['Z', 'e', 'b', 'a', 'r', 'e', 'r', 'u', 'n']
[ ]:
words = ["Zebras", "are", "running"]

la = []

# write here

print(la)

Show solution

[ ]:
words = ["Zebras", "are", "running"]

la = []

la.extend(words[0][:3])
la.extend(words[1][:3])
la.extend(words[2][:3])

print(la)

insert method#

insert MODIFIES the list by inserting an element at a specific index - all elements starting from that index will be shifted of one position to the right.

[31]:
     #0 1 2 3
la = [6,7,8,9]
[32]:
la.insert(2,55)  # insert the number 55 at index 2
[33]:
la
[33]:
[6, 7, 55, 8, 9]
[34]:
la.insert(0,77)  # insert the number 77 at index 0
[35]:
la
[35]:
[77, 6, 7, 55, 8, 9]

We can insert after the end:

[36]:
la.insert(6,88)  # insert the number 88 at index 6
[37]:
la
[37]:
[77, 6, 7, 55, 8, 9, 88]

Note that if we go beyond the end, the element is placed right after the end and no empty cells are created:

[38]:
la.insert(1000,99)  # insert number 99 at index 7
[39]:
la
[39]:
[77, 6, 7, 55, 8, 9, 88, 99]

QUESTION: Given any list x, what does this code produce? Can we rewrite it in some other way?

x.insert(len(x),66)
  1. produces a new list (which one?)

  2. modifies x (how?)

  3. an error

ANSWER: 2 - the code MODIFIES the list x by adding the element 66 at the end. The code is then equivalent to code

x.append(66)

QUESTION: What does the following code produce?

la = [3,4,5,6]
la.insert(0,[1,2])
print(la)
  1. prints [1,2,3,4,5,6]

  2. an error (which one?)

  3. something else (what?)

ANSWER: 3 - the code inserts in la the list [1,2] as zero-th element. The print will then show [[1, 2], 3, 4, 5, 6]

QUESTION: What does the following code produce?

la = [4,5,6]
la.insert(0,1,2,3)
print(la)
  1. prints [1,2,3,4,5,6]

  2. an error (which one?)

  3. something else (what?)

ANSWER: 2 - an error, because we can only pass 2 parameters to insert, the insertion index and the single object to insert.

QUESTION: What does the following code produce?

la = [4,5,6]
lb = la.insert(0,3)
lc = lb.insert(0,2)
ld = lc.insert(0,1)
print(ld)
  1. prints [1,2,3,4,5,6]

  2. an error (which one?)

  3. something else (what?)

ANSWER: 2 - an error: like almost all list methods, insert returns None, so by writing lb = la.insert(0,3) we are associating None to lb, so when Python in the next line encounters lc = lb.insert(0,2) and tries to execute None.insert(0,2) it will complain because None not being a list doesn’t have the insert method.

Exercise - insertando#

Given the list

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

write some code which MODIFIES the list by using only calls to insert. After your code, la should appear like this:

>>> print(la)
[7, 70, 90, 6, 8, 80, 5, 6, 50]
[ ]:
la = [7,6,8,5,6]

# write here

print(la)

Show solution

[ ]:
la = [7,6,8,5,6]

la.insert(3,80)
la.insert(1,90)
la.insert(1,70)
la.insert(len(la),50)

print(la)

WARNING: calling insert is much slower than append !!

A call to insert rewrites all the cells after the insertion point, while append instead adds only one cell. Given the computer is fast, very often we don’t realize the difference, but whenever possible try writing code using append instead of insert, especially if you have to write programs which operate on big amounts of data.

Exercise - insappend#

This code takes as input an empty list la and a list of numbers lb. Try to understand what it does, and rewrite it using some append.

[41]:
la = []
lb = [7,6,9,8]
la.insert(0,lb[0]*2)
la.insert(0,lb[1]*2)
la.insert(0,lb[2]*2)
la.insert(0,lb[3]*2)
print(la)
[16, 18, 12, 14]
[ ]:
la = []
lb = [7,6,9,8]

# write here

print(la)

Show solution

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

la.append(lb[-1]*2)
la.append(lb[-2]*2)
la.append(lb[-3]*2)
la.append(lb[-4]*2)

print(la)

pop method#

pop method does two things: when called without arguments MODIFIES the list by removing the last element, and also RETURNS the removed element:

[43]:
basket = ['melon','strawberry', 'apple']
[44]:
basket.pop()
[44]:
'apple'
[45]:
basket
[45]:
['melon', 'strawberry']
[46]:
basket.pop()
[46]:
'strawberry'
[47]:
basket
[47]:
['melon']

Since the last element is returned by pop, we can also assign it to a variable:

[48]:
fruit = basket.pop()

Note we don’t see no result printed because the returned element was assigned to the variable fruit:

[49]:
fruit
[49]:
'melon'

We also notice that basket was MODIFIED indeed:

[50]:
basket
[50]:
[]

If you further call pop on an empty list you will get an error:

basket.pop()
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
<ipython-input-67-086f38c9fbc0> in <module>()
----> 1 basket.pop()

IndexError: pop from empty list

Optionally, to remove an element from a specific position we can pass pop an index from 0 INCLUDED to the length of the list EXCLUDED:

[51]:
#           0           1             2        3
tools = ['hammer', 'screwdriver', 'plier', 'hammer']
[52]:
tools.pop(2)
[52]:
'plier'
[53]:
tools
[53]:
['hammer', 'screwdriver', 'hammer']

QUESTION: Have a look at following code snippets, and for each of them try to guess the result it produces (or if it gives an error):

  1. la = ['a']
    print(la.pop())
    print(la.pop())
    
  2. la = [4,3,2,1]
    print(la.pop(4))
    print(la)
    
  3. la = [1,2,3,4]
    print(la.pop(3))
    print(la)
    
  4. la = [1,2,3,4]
    print(la.pop(-1))
    print(la)
    
  5. s = 'raw'
    print(s.pop())
    print(s)
    
  6. la = ['so', 'raw']
    print(la.pop())
    print(la)
    
  7. la = ['a', ['a']]
    print(la.pop())
    print(la)
    

Exercise - popcorn#

Given the list corn of exactly 4 characters, write some code which transfers in reverse order all the characters from corn to another list box which is initially empty.

  • DO NOT use methods like reverse or functions like reversed

  • Your code must work with any list corn of 4 elements

Example - given:

corn = ['G','u','r','u']
box = []

after your code, it must result:

>>> print(corn)
[]
>>> print(box)
['u', 'r', 'u', 'G']
[ ]:
corn = ['G','u','r','u']
box = []

# write here

print(box)

Show solution

[ ]:
corn = ['G','u','r','u']
box = []

box.append(corn.pop())
box.append(corn.pop())
box.append(corn.pop())
box.append(corn.pop())

print(box)

Exercise - zonzo#

Given a list la containing some characters, and a list lb containing exactly two positions in ascending order, write some code which eliminates from la the characters at positions specified in lb.

  • WARNING: by calling pop the first time you will MODIFY la, so the index from the second element to eliminate will need to be properly adjusted !

  • DO NOT create new lists, so no rows beginning with la =

  • Your code must work with any la and any lb of two elements

Example - given:

#      0   1   2   3   4
la = ['z','o','n','z','o']
lb = [2,4]

at position 2 in la we find the n and at 4th the o, so after your code it must result:

>>> print(la)
['z', 'o', 'z']
[ ]:
#      0   1   2   3   4
la = ['z','o','n','z','o']
lb = [2,4]

# write here

print(la)

Show solution

[ ]:
#      0   1   2   3   4
la = ['z','o','n','z','o']
lb = [2,4]

la.pop(lb[0])
la.pop(lb[1]-1)

print(la)

reverse method#

reverse method MODIFIES the list on which it is called by inverting the order of elements.

Let’s see an example:

[56]:
la = [7,6,8,4]
[57]:
la.reverse()
[58]:
la
[58]:
[4, 8, 6, 7]

WARNING: reverse RETURNS NOTHING!

To be precise, it returns None

[59]:
lb = [7,6,8,4]
[60]:
x = lb.reverse()
[61]:
print(x)
None
[62]:
print(lb)
[4, 8, 6, 7]

QUESTION: Which effect does the following code produce?

s = "transatlantic"
s.reverse()
print(s)
  1. an error (which one?)

  2. prints the string in reverse

ANSWER: .reverse() is a method ONLY present in LISTS, so by using it on strings we will get an error. And we have to expect it, as reverse MODIFIES the object on which it is called and since strings are immutable no string method can possibly modify the string on which it is called.

QUESTION: If x is some list, which effect does the following produce?

x.reverse().reverse()
  1. changes the list (how?)

  2. it doesn’t change the list

  3. generates an error (which one?)

ANSWER: 3 - generates an error, because reverse() returns None which is not a list so it doesn’t have reverse() method.

Exercise - good manners#

Write some code which given two lists la and lb MODIFY la adding all the elements of lb and then reversing the whole list.

  • you code must work with any la and lb

  • DO NOT modify lb

Example - given:

la = ['g','o','o','d']
lb = ['m','a','n','n','e','r','s']

After your code, it must print:

>>> print('la=',la)
la= ['s', 'r', 'e', 'n', 'n', 'a', 'm', 'd', 'o', 'o', 'g']
>>> print('lb=',lb)
lb= ['m', 'a', 'n', 'n', 'e', 'r', 's']
[ ]:
la = ['g','o','o','d']
lb = ['m','a','n','n','e','r','s']

# write here

print('la=',la)
print('lb=',lb)

Show solution

[ ]:
la = ['g','o','o','d']
lb = ['m','a','n','n','e','r','s']

la.extend(lb)
la.reverse()

print('la=',la)
print('lb=',lb)

Exercise - precious things#

Given two lists la and lb write some code which PRINTS a list with the elements of la and lb in reverse order.

  • DO NOT modify la and DO NOT modify lb

  • your code must work with any list la and lb

Example - given:

la = ['p','r','e','c','i','o','u','s']
lb = ['t','h','i','n','g','s']

After your code, it must print:

['s', 'g', 'n', 'i', 'h', 't', 's', 'u', 'o', 'i', 'c', 'e', 'r', 'p']
[ ]:
la = ['p','r','e','c','i','o','u','s']
lb = ['t','h','i','n','g','s']

# write here

Show solution

[ ]:
la = ['p','r','e','c','i','o','u','s']
lb = ['t','h','i','n','g','s']

lc = la + lb  # the + creates a NEW list
lc.reverse()
print(lc)

Exercise - powers#

The following code uses some insert which as we already said it is not very efficient. Try to understand what it does, and rewrite it using only append and reverse

  • your code must work for any value of x

[65]:
x = 2
la = [x]
la.insert(0,la[0]*2)
la.insert(0,la[0]*2)
la.insert(0,la[0]*2)
la.insert(0,la[0]*2)
print(la)
[32, 16, 8, 4, 2]
[ ]:
x = 2
la = [x]

# write here

print(la)

Show solution

[ ]:
x = 2
la = [x]

la.append(la[-1]*2)
la.append(la[-1]*2)
la.append(la[-1]*2)
la.append(la[-1]*2)
la.reverse()

print(la)

sort method#

If a list contains homogenous elements, it is possible to sort it rapidly with the sort method, which MODIFIES the list on which it is called (also called sorting in-place):

[67]:
la = [8,6,7,9]
[68]:
la.sort()  # NOTE: sort returns nothing !!!
[69]:
la
[69]:
[6, 7, 8, 9]

Strings are also sortable

[70]:
lb = ['Boccaccio', 'Alighieri', 'Manzoni', 'Leopardi']
[71]:
lb.sort()
[72]:
lb
[72]:
['Alighieri', 'Boccaccio', 'Leopardi', 'Manzoni']

A list with non-comparable elements it’s not sortable, and Python will complain:

[73]:
lc = [3,4,'cabbage',7,'potatoes']
>>> lc.sort()

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-288-0cabfae30939> in <module>
----> 1 lc.sort()

TypeError: '<' not supported between instances of 'str' and 'int'

Optionally, for reverse order you can pass the parameter reverse=True :

[74]:
la = [4,2,5,3]
la.sort(reverse=True)
[75]:
la
[75]:
[5, 4, 3, 2]

Custom sorting#

If you have custom needs like for example a lists of strings in the format 'name surname' that you want to sort according to the surname, you should use optional parameter key with lambda functions, see Python docs

Exercise - numlist#

Given the list la = [10, 60, 72, 118, 11, 71, 56, 89, 120, 175]

  1. finds the min, max and the median value (HINT: sort it and extract the right values)

  2. create a list only with elements at even indexes (i.e. [10, 72, 11, ..], note that “..” means the list is still not complete!) and ricalculates the values of min, max and median

  3. redo the same with the elements at odd indexes (i.e. [60, 118,..])

You should obtain:

original:    [10, 60, 72, 118, 11, 71, 56, 89, 120, 175]
even:        [10, 72, 11, 56, 120]
odd:         [60, 118, 71, 89, 175]

sorted:      [10, 11, 56, 60, 71, 72, 89, 118, 120, 175]
sorted even: [10, 11, 56, 72, 120]
sorted odd:  [60, 71, 89, 118, 175]

original:    Min: 10  Max. 175  Median:  72
even:        Min: 10  Max. 120  Median:  56
odd:         Min: 60  Max. 175  Median:  89
[ ]:
la = [10, 60, 72, 118, 11, 71, 56, 89, 120, 175]

# write here

Show solution

[ ]:
la = [10, 60, 72, 118, 11, 71, 56, 89, 120, 175]

even = la[0::2]   # we take only elements at even indeces
odd = la[1::2]    # we take only elements at odd indeces

print("original:   " , la)
print("even:       ", even)
print("odd:        ", odd)

la.sort()
even.sort()
odd.sort()

print()
print("sorted:     " , la)
print("sorted even:" , even)
print("sorted odd: " , odd)
print()
print("original:    Min:", la[0], " Max." , la[-1], " Median: ", la[len(la) // 2])
print("even:        Min:", even[0], " Max." , even[-1], " Median: ", even[len(even) // 2])
print("odd:         Min:", odd[0], " Max." , odd[-1], " Median: ", odd[len(odd) // 2])

join - build strings from lists#

Given a string to use as separator, and a sequence like for example a list la which only contains strings, it’s possible to concatenate them into a (new) string with join method:

[77]:
la = ["When", "the", "sun", "raises"]

'SEPARATOR'.join(la)
[77]:
'WhenSEPARATORtheSEPARATORsunSEPARATORraises'

As separator we can put any character, like a space:

[78]:
' '.join(la)
[78]:
'When the sun raises'

Note the original list is not modified:

[79]:
la
[79]:
['When', 'the', 'sun', 'raises']

QUESTION: What does this code produce?

''.join(['a','b','c']).upper()
  1. an error (which one?)

  2. a string (which one?)

  3. a list (which one?)

ANSWER: 2: it produces the string 'ABC': first it takes all characters from the list ['a','b','c'] and it joins them with empty space '' separator to form 'abc', then this string is set all uppercase with upper().

QUESTION: What does this code produce?

'a'.join('KRT') + 'E'
  1. a string (which one?)

  2. an error (which one?)

  3. a list (which one?)

ANSWER: 1: produces the string 'KaRaTE' - we said that join takes as input a sequence, so we are not bounded to pass lists but we can directly pass any string, which is a character sequence. join will then interval each character in the string with the separator we provide before the dot.

QUESTION: What does this code produce?

'\''.join('mmmm')
  1. an error (which one?)

  2. a string (which one?)

ANSWER: 2: \' is an escape sequence which represents the single character apex ', so we will obtain m'm'm'm

QUESTION: Given any string s and a list of strings la of at least two characters, the following code will always give us the same result - which one? (think about it, and if you don’t know how to answer try putting random values for s and la)

len(s) <= len(s.join(la))
  1. an errore (which one?)

  2. a stringa (which one?)

  3. something else (what?)

ANSWER: 3: the code will always produce the boolean True because s.join(la) produces a string containing all the strings in la alternated with the string s. So the length of this string will always be greater or equal to the length of s: by comparing the two lengths with <= operator, we will always obtain the boolean True.

Example:

s = "ab"
la = ['uief','cb','sd']
len(s) <= len(s.join(la))

Exercise - barzoletta#

Given the string

sa = 'barzoletta'

write some code which creates a NEW string sb by changing the original string in such a way it results:

>>> print(sb)
'barzelletta'
  • USE the method insert and cell reassignment

  • NOTE: you cannot use them an a string, because it is IMMUTABLE - you will then first convert the string to a list

[ ]:
sa = 'barzoletta'

# write here

Show solution

[ ]:
sa = 'barzoletta'

la = list(sa)
la[4] = 'e'
la.insert(5,'l')
sb = ''.join(la)
print(sb)

Exercise - dub dab dib dob#

Write some code which given a list of strings la, associates to variable s a string with the concatenated strings, separating them with a comma and a space.

Example - given:

la = ['dub', 'dab','dib', 'dob']

After your code, you should obtain this:

>>> print(s)
dub, dab, dib, dob
>>> len(s)
18
[ ]:
la = ['dub', 'dab','dib', 'dob']

# write here

Show solution

[ ]:
la = ['dub', 'dab','dib', 'dob']

s = ', '.join(la)

print(s)
len(s)

Exercise - ghirigori#

Given a list of strings la and a list with three separators seps, write some code which prints the elements of la separated by first separator, followed by the second separator, followed by the elements of la separated by the third separator.

  • your code must work with any list la and seps

Example - given:

la = ['ghi','ri','go','ri']
seps = [',','_','+']

After your code, it must print:

ghi,ri,go,ri_ghi+ri+go+ri
[ ]:
la = ['ghi','ri','go','ri']
seps = [',','_','+']

# write here

Show solution

[ ]:
la = ['ghi','ri','go','ri']
seps = [',','_','+']

print(seps[0].join(la) + seps[1] + seps[2].join(la))

Exercise - welldone#

Given the list:

la = ["walnut", "eggplant", "lemon", "lime", "date", "onion", "nectarine", "endive" ]:
  1. Create another list (call it new) containing the first character of every element of la

  2. Add a space to new at position 4 and attach an exclamation mark ('!') at the end

  3. Print the list

  4. Print the list content by joining all elements with an empty space

You should get:

['w', 'e', 'l', 'l', ' ', 'd', 'o', 'n', 'e', '!']

 well done!
[ ]:
la = ["walnut", "eggplant", "lemon", "lime", "date", "onion", "nectarine", "endive" ]

# write here

Show solution

[ ]:
la = ["walnut", "eggplant", "lemon", "lime", "date", "onion", "nectarine", "endive" ]

new = []
new.append(la[0][0])
new.append(la[1][0])
new.append(la[2][0])
new.append(la[3][0])
new.append(la[4][0])
new.append(la[5][0])
new.append(la[6][0])
new.append(la[7][0])

new.insert(4," ")
new.append("!")

print(new)
print("\n", "".join(new))

copy method#

If we want to copy a mutable data structure, we can use the .copy() method, which performs a so-called shallow copy. Let’s see what it means.

Let’s start with a simple case, for example a list of immutable objects like strings we can visualize in Python Tutor:

[84]:
satellites = ["Hubble", "Sputnik 1"]

copia = satellites.copy()

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

We clearly see how a completely new memory region was created. If we later try to modify the copy, we will see the original is not changed:

[85]:
satellites = ["Sputnik 1", "Hubble"]

my_copy = satellites.copy()

my_copy.append("James Webb")

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

copy is shallow#

So far, we didn’t notice any particular problem. But what happens if we try copy() on a list which contains other lists, in other words, other mutable elements, and then try mutating one of the two?

[86]:

biglistA = [ ['Pay', 'attention'], ['to', 'where'], ['the', 'arrows', 'point to'] ] biglistB = biglistA.copy() biglistA[2][0] = 'CAREFUL!' # we write into the original... print(biglistA) print(biglistB) jupman.pytut()
[['Pay', 'attention'], ['to', 'where'], ['CAREFUL!', 'arrows', 'point to']]
[['Pay', 'attention'], ['to', 'where'], ['CAREFUL!', 'arrows', 'point to']]
[86]:
Python Tutor visualization

Note we have two big lists containing cells that point to shared sublists: as a matter of fact, by writing into a subcell of biglistA we also write into biglistB!

In other words, .copy() performs only a shallow copy, for a proper deep copy we will need to find some other way!

deepcopy function#

To avoid sharing problems we can use the so-called deep copy, available in the function deepcopy from module copy

WARNING: deepcopy IS NOT a list method!

Let’s try again the example with copy.deepcopy: we will now get completely distinct data structures:

[87]:
# first we import `copy`, which is a PYTHON MODULE
import copy

biglistA = [ ['Pay', 'attention'],
             ['to', 'where'],
             ['the', 'arrows', 'point to']
]

# then we call its function deepcopy, passing the parameter biglistA:

biglistB = copy.deepcopy(biglistA)
biglistA[2][0]  = 'CAREFUL!'  # we write into the original...

print(biglistA)
print(biglistB)

jupman.pytut()
[['Pay', 'attention'], ['to', 'where'], ['CAREFUL!', 'arrows', 'point to']]
[['Pay', 'attention'], ['to', 'where'], ['the', 'arrows', 'point to']]
[87]:
Python Tutor visualization

Continue#

Go on with Lists 4 - Search methods