# 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:


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']
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