from IPython.core.display import HTML

HTML(open("custom.html", "r").read())
Creative Commons License This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Copyright (C) 2014-2023 Scientific IT Services of ETH Zurich,
Contributing Authors: Uwe Schmitt, Mikolaj Rybniski

6. More about Python container types¶

Lists¶

Python lists collect data, types may be mixed and the list may be as long as your computers memory allows.

There are not type restrictions for the elements of a list

li = [0, 1.0, 2, True, "abc", [1, 2, 3]]
print(li)
[0, 1.0, 2, True, 'abc', [1, 2, 3]]

Some useful, but not all list methods:

words = ["this", "is", "a", "list"]
words.append("in")
words.append("python")

print(words)
['this', 'is', 'a', 'list', 'in', 'python']

Comment: string methods do not change the corresponding string in-place but return a result. append returns None but changes the list in place:

words = ["this", "is", "a", "list"]
words_new = words.append("in")

print("words_new is", words_new)
print("words is", words)
words_new is None
words is ['this', 'is', 'a', 'list', 'in']

In case you want to append multiple elements to a list, you can use .extend which takes a list of items:

words = ["this", "is", "a", "list"]
words.extend(["in", "python"])
print(words)
['this', 'is', 'a', 'list', 'in', 'python']

Please be aware that numbers.append would give us a very different result here, we would append a list of numbers in this case:

words = ["this", "is", "a", "list"]
words.append(["in", "python"])
print(words)
['this', 'is', 'a', 'list', ['in', 'python']]
print(len(words))
5
words = ["this", "is", "a", "list"]
words.sort()
print(words)
['a', 'is', 'list', 'this']

To find the position of an element:

print(words.index("list"))
2
print(words.index("python"))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[10], line 1
----> 1 print(words.index("python"))

ValueError: 'python' is not in list

Element access, similar to strings:

 Indexing starts with zero, upper limits are exclusive, negative indexes begin at the end
words = ["this", "is", "a", "list"]
print(words[0], words[-1])
this list

List slicing, similar to strings:

print(words[1:-1])
['is', 'a']

In contrast to Python strings, which are immutable ("const"), you can use index access as well as slicing for manipulation of a list

words[3] = "parrot"
print(words)
['this', 'is', 'a', 'parrot']

Deletion of parts of a list works like this:

del words[1:3]
print(words)
['this', 'parrot']

Lists of strings¶

In the chapter about strings we did not mention two important string methods: str.split and str.join since we did not introduce lists back then.

The str.split method splits a string based on a given separator and returns a list of strings:

text = "this is some text, isn't it?"
print(text.split(" "))
print(text.split("is"))
['this', 'is', 'some', 'text,', "isn't", 'it?']
['th', ' ', ' some text, ', "n't it?"]

Without arguments, str.split splits at white spaces (spaces, line breaks and tabs):

text = """this is some text,
isnt't it?
"""

print(text.split())
['this', 'is', 'some', 'text,', "isnt't", 'it?']

Comment: in case you want to split at different characters, e.g. at spaces and commas, the split function to the re module from the standard library can be used.

The inverse operation is called join: part.join(list_of_strings) will concatenate all strings from list_of_strings and insert the separator string part between them:

text = "this is some text, isn't it?"
parts = text.split()
print(parts)
back = " ".join(parts)
print(back)
['this', 'is', 'some', 'text,', "isn't", 'it?']
this is some text, isn't it?
print("".join(parts))
print(" - ".join(parts))
thisissometext,isn'tit?
this - is - some - text, - isn't - it?

Comment: In case you want to concatenate many long strings, using "".join(...) can be significantly faster than using + !!!

Tuples¶

tuples are "immutable" lists. Use round instead of square brackets:

  • mixed types allowed too
  • slicing works
a = (1, 3, 5)
print(a)
print(type(a))
(1, 3, 5)
<class 'tuple'>

Rules of thumb:

  • Use lists for collecting entities of same type
  • Use tuples for grouping data of different type (a bit like structs in C/C++)

For example like this:

person_1 = ("jesus", 2015)  # name + age
person_2 = ("donald", 7)

people = [person_1, person_2]

Slicing / index access again:

tp = (1, 2, (1, 2), "")
print(tp[1:-1])
(2, (1, 2))

Empty tuple is (), for one element tuples use (x,) notation:

print((1,))  # prints tuple with one element
print((1))  # prints integer number 1
(1,)
1

Membership test¶

The keyword in tests for membership:

print(2 in [0, 1, 2, 3])
True
print(2 in (0, 1, 2, 3))
True

For strings in tests for substrings:

print("ab" in "asdfabc")
True

Negation: you can either negate the result of the in expression:

print(not (2 in (1, 2, 3)))
False

or write in a more readable way:

print(2 not in (1, 2, 3))
False

Typical pattern¶

lists are fundamental in Python and transforming lists is seen often Python programs.

Task: remove all odd numbers from a list of numbers.

One might be tempted to implement the functions as follows:

def remove_odd_numbers(li):
    for number in li:
        if number % 2 == 1:
            li.remove(number)
    return li


print(remove_odd_numbers([2, 1, 1, 1, 1, 4]))
[2, 1, 1, 4]

As you can see this did not work. The reason is that we changed the list while iterating over it.

Pattern: In order to transform a list a into a list b it is common to start with b = [] and then incrementally update b based on the elements of a:

def remove_odd_numbers(li):
    result = []
    for number in li:
        if number % 2 == 1:
            continue
        result.append(number)
    return result


print(remove_odd_numbers([2, 1, 1, 1, 1, 4]))
[2, 4]

Exercises block 6¶

  • Reread the examples above carefully.

Check questions¶

  1. Use pen and paper and Python help function to determine is the value of a after executing:

     a = [0, 1]
     a.append(2)
     a.append(a[:2])
     a.insert(1, a[-1])
     a.extend(a[1])
    

Programming exercise¶

  1. Write a function which takes a list of numbers and returns a new list which contains the squares of the odd numbers from the input list.

Optional programming exercises*¶

  1. Write a function which flattens a nested list of lists (no deeper nesting), so flatten([[1], [2,3], [4, 5, 6]]) == [1, 2, 3, 4, 5, 6]).

  2. Look up the definition of the fibionacci number sequence and write a function which takes an integer number n and computes a list of the first n fibionacci numbers (hint: start with [1, 1] and extend the list).

  3. What is the output of the following program:

    def f(x, a=[]):
        a.append(x)
        return len(a)
    
    print(f(0))
    print(f(1))
    
    
    

You might use additional print statements to inspect what's going on here.