Hands-on: Python Fundamentals -- Basic Containers: Lists

Objectives:

Upon completion of this lesson, you should be able to:

  • Describe the characteristics of the list in Python

  • Perform basic operations with lists including creation, concatenation, repetition, slicing, and traversing

There are Four basic containers in Python: list, tuple, set and dictionary. We will first consider possibly the "simplest" one -- list.

The list data structure

  • In Python, a list is a mutable sequence of values. Mutable means that we can change separate entries within a list
  • Each value in the list is an element or item
  • Elements can be any Python data type
  • Lists can mix data types
  • Elements can be nested lists

Creating lists

  • Lists are created with comma-separated values inside brackets.
In [ ]:
numbers = [1, 2, 3, 4]
print numbers
cheeses = ['swiss', 'cheddar',
           'ricotta', 'gouda']
print cheeses

Creating lists

  • You can mix types
In [ ]:
mixed = [1, 'a', 3.45]
print mixed
  • and lists with single items (or no items) are lists
In [ ]:
single = ['z']
print single, type(single)
empty = []
print empty
  • you can create a list for any iterable ( by simply passing it to the list() function
In [ ]:
print list("I am a string")

and more often you will see the use of str.split() method to create list of separate "words":

In [ ]:
print "I am a string".split()

Read lines from a file

In Python it is very easy to read external files. Even though we haven't covered that yet, here is a little taster, which reads first 10 lines from a file in the parent directory:

In [ ]:
with open("../README.md") as f:
    print f.readlines()[:10]

Excercise

Improve upon that snippet above, to print only the lines a hash ("#") (you might like to use str.startwith(), str.lstrip(), str.rstrip() functions)

Hints: list is "iterable" so recall how we loop conveniently through an iterable with a for loop

Extra credit

Enhance to print only the lines which come from the "Code of conduct" sub-section

Repeating a list

  • Use the * operator to expand a list via repeatition:
In [ ]:
meat = ['spam']*4
print meat
print [1, 2, 3]*3

Microquiz

With what other basic type * operator resulted in a very similar behavior?

List indexing

  • Elements within a list are indexed (starting with 0)
In [ ]:
print cheeses[0]

Microquiz

Why do you think indexing in generic programming languages (C, C++, ...) as a rule starts with 0?

  • Lists are mutable
In [ ]:
cheeses[0] = 'Feta'
print cheeses

Microquiz/Excercise

Are strings mutable?

Slicing a list

  • Like strings and other sequences, lists can be sliced.
  • Slicing syntax: l[start:stop:stride]

All slicing parameters are optional:

In [ ]:
l = range(1, 6)
print(l)
In [ ]:
print(l[3:])
In [ ]:
l[:3]
In [ ]:
l[::2]

Note that

  • l[start:stop] contains the elements with indices i such as start <= i < stop (i ranging from start to stop-1), i.e. includes start but does not includes stop
  • Thererore, l[start:stop] has (stop-start) elements.

Excercise #1

stride can be negative. Explore on the above example effects -- what does having negative stride result in?

In [ ]:
 

Excercise #2

stop can also be negative. Explore on the above example effects -- what does having negative stop result in?

In [ ]:
 

Changing a slice

  • You can use slices to modify the contents of a list:
In [ ]:
roster = ['Meghan', 'Tricia', 'Juan',
          'Alton', 'Darrel', 'Jen']
print roster
roster[1:3] = ['Sam', 'Kerri'] # Assign multiple entries at once
print roster
roster[3:5] = ['Tayla'] # Assigning less than selected results in "shrinking" of the list
print roster

Inserting elements

  • Slice notation
In [ ]:
roster[2:2] = ['Dana', 'Ryan']
print roster

Deleting elements

  • Setting a slice to empty list will delete those elements:
In [ ]:
roster[3:5] = []
print roster
  • Or you can use the del keyword:
In [ ]:
del roster[2:3]
print roster

The insert method

  • Lists have an insert method:
In [ ]:
roster.insert?
  • Let's see it in action:
In [ ]:
roster.insert(2, 'Jakob')
print roster

The append and extend methods

  • The append method adds individual items to a list:
In [ ]:
roster.append('Tonya')
print roster
  • The extend method adds a list to the end of an existing list:
In [ ]:
adds = ['Ian', 'Stacie']
roster.extend(adds)
print roster

Microquiz

  • Should you .append or .extend a list with 123?

Extending a list with operators

  • Can also use += operator
In [ ]:
roster += ['Anya']
print roster
  • Or simply the + operator
In [ ]:
a = [1, 2, 3]
b = [4, 5, 6]
c = a + b
print a, b, c
  • The + operator returns a new list that is a concatenation of two lists

List assignment and aliasing

  • The slice operator returns a copy of a list
In [ ]:
a = [1, 2, 3, 4]
b = a
c = a[:]
a[2] = 9
print a, b, c

Traversing a list

  • There are many ways to loop over a list, and such functions as enumerate, zip and others could be of great help:
In [ ]:
for index in range(len(roster)):
    print roster[index]
In [ ]:
for student in roster:
    print student
In [ ]:
for index, student in enumerate(roster):
    print index, student    

Note how enumerate keeps track of the index and the item, which can come in handy -- you don't really need to use for i in range(len(l)) to traverse the list


Microquiz

  • What does this do?
empty = []
for x in empty:
    print(x)

Excercise

I have mentioned above zip function which I promised would help to make the world a better place. So -- use learnt knowedge to figure out what it does and come up with a usecase/example for its usage.

In [ ]:
 

Nested lists

  • You can nest lists of lists of lists...
In [ ]:
nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print nested
print nested[0]
print nested[0][1]

Traversing nested lists

  • Each nested list can be traversed in the same way as an individual list:

  • By index (less efficient, often ugly):

In [ ]:
for i in range(len(nested)):
    for j in range(len(nested[i])):
        print nested[i][j]
  • Or item (faster, reads better)
In [ ]:
for nest in nested:
    for item in nest:
        print item

Using lists: cumulate.py

  • You can pass lists as arguments to functions:
In [ ]:
def cumulate(seq):
    c_sum = 0
    for item in seq:
        c_sum += item
    return c_sum

a = [12, 78, 32, 82]
s = cumulate(a)
print s

Returning lists from functions

  • You can return lists from functions:
In [ ]:
def only_upper(t):
    res = []
    for s in t:
        if s.isupper():
            res.append(s)
    return res

text = 'Bold cOlOrs Make for Easy Reading'
secret = only_upper(text)
print secret

Modifying lists in functions

  • In Python, arguments are passed by reference
  • The parameter in the function is an alias for the argument that was passed in
  • If a mutable object is changed inside the function, it is also changed outside the function!

Example: byref.py

  • Here we illustrate modifying a list in a function:
In [ ]:
def change(seq):
    print 'Passed in: ' + str(seq)
    seq.append('new item')
    print 'Changed to: ' + str(seq)

original = [1, 2, 3]
print original
change(original)
print original

Example: byref2.py

In [ ]:
def change(seq):
        print 'Passed in: ' + str(seq)
        seq.append('new item')
        print 'Changed to: ' + str(seq)
        new_seq = ['created', 'in', 'function']
        print 'New seq: ' + str(new_seq)

original = [1, 2, 3]
new_seq = ['outside','the','function']
print original
change(original)
print original
print new_seq

WARNING: Do not use [] list as default keyword argument

In [ ]:
def change(seq=[]):
    seq.append(1)
    return seq

original = [1, 2, 3]
for i in range(4):
    print change()

Instead define default to None and assign within the function, thus a new list would be created on each call:

In [ ]:
def change(seq=None):
    seq = seq or []
    seq.append(1)
    return seq

original = [1, 2, 3]
for i in range(4):
    print change()

List comprehensions

Very powerful technique allowing for efficient construction of new lists.

It is usually prefered over looping regularly to fill up pre-created list. Compare:

In [ ]:
l = []
for s in "something creative":
    l.append(s)

to

In [ ]:
l = [s for s in "something creative"]
print(l)

Miniquiz

well -- above example is boring, since we could accomplish the same by ...?

We could store multiple values per each item right away:

In [ ]:
l = [(i, s, len(s)) for i, s in enumerate("something creative")]
print(l)

Conditionals in the comprehensions

  • List comprehension allows to specify a condition if a given item should be included in the resultant list:
In [ ]:
print [s for s in "something creative" if s in "eai"]

Excercise

Recall the example above for reading section headings from README.md. Create a list comprehension which would create a list of line headings.

WARNING: variables defined in list comprehensions are local to the entire code block

In [ ]:
[x for x in "123"]
print x

and they would override values defined locally before:

In [ ]:
i = "precious"
print i
[i for i in ["evil"]]
print i