Basics of lists


In [56]:
from __future__ import print_function

In [14]:
l1 = list() 
l2 = []

print(l1)
print(l2)


[]
[]

The length of a list is acquired by the len functino:


In [15]:
print(len(l1))
print(len(l2))


0
0

Lists can be initialised if its values are known at run time:


In [16]:
l3 = [1, 2, 3]
print(l3)
print(len(l3))


[1, 2, 3]
3

Appending and extending lists


In [17]:
l1.append(1)
print(l1)
l1.append(10)
print(l1)


[1]
[1, 10]

In [18]:
l2.append(100)
print(l2)


[100]

In [19]:
print(l1)
print(l2)
print(l1 + l2)


[1, 10]
[100]
[1, 10, 100]

Note that the + operator does not modify the lists inplace, rather it constructs a new list from the values of l1 and l2.

If inplace extension is required, the .extend member function should be used:


In [20]:
l1.extend(l2)
print(l1)
print(l2)


[1, 10, 100]
[100]

Items can be removed from the lists too, with the pop function. When called, the pop function removes the last element of the list. If an argument is passed into the pop function, the element contained at that position in the list is removed, e.g.


In [21]:
l2 = [0, 1, 2, 3, 4, 5, 6]

l2.pop(1)
print(l2)

l2.pop()
print(l2)


[0, 2, 3, 4, 5, 6]
[0, 2, 3, 4, 5]

In [22]:
print(l1)


[1, 10, 100]

List comprehension

A powerful feature of the python language is that iteration over iterables can be done concisely in one line using list comprehension. This technique allows copies, transformation, and filtering of lists, as follows:


In [23]:
l3 = [-2, -1, 0, 1, 2]
print(l3)
print(len(l3))


[-2, -1, 0, 1, 2]
5

In [24]:
print([el for el in l3])


[-2, -1, 0, 1, 2]

In [25]:
# Return the positive elements 
print([el for el in l3 if el > 0])


[1, 2]

In [26]:
# Return the negative elements 
print([el for el in l3 if el < 0])


[-2, -1]

In [27]:
# Multiply the elements by two
print([el * 2 for el in l3])


[-4, -2, 0, 2, 4]

In [28]:
# Multiply filtered elements by two
print([el * 2 for el in l3 if el <= 1])


[-4, -2, 0, 2]

Generaters

In python (especially Python3), generaters are used in many cases. These are objects that perform lazy evaluation (i.e. code is executed when required, not when composed). We will come across these later.

 Filtering and mapping from in-build functions

The operations above can also be achieved with the builtin filter and map functions. The above operations are demonstrated below


In [30]:
def is_positive(el): 
    return el > 0

print(l3)
print(filter(is_positive, l3))
print(list(filter(is_positive, l3)))  # python 3


[-2, -1, 0, 1, 2]
<filter object at 0x112250080>
[1, 2]

Anonymous inline functions (lambdas) can be used if the is_positive function isn't available


In [32]:
# Return the positive elements
print(list(filter(lambda el: el > 0, l3)))


[1, 2]

In [33]:
# Return the non-positive elements
print(list(filter(lambda el: el <= 0, l3)))


[-2, -1, 0]

In [34]:
# Return elements outside of a range
print(list(filter(lambda el: el < -1 or el > 1, l3)))


[-2, 2]

In [35]:
# Return the elements found within a range (note the mathematical notation)
print(list(filter(lambda el: -1 <= el <= 1, l3)))


[-1, 0, 1]

Mapping functions to lists

The map function takes a function pointer and iterables as parameters. The following replicates the list comprehension mapping methods described earlier


In [41]:
print([abs(el) for el in l3])


[2, 1, 0, 1, 2]

In [43]:
print(list(map(abs, l3)))


[2, 1, 0, 1, 2]

In [44]:
def add_one(item): 
    return item + 1

print(list(map(add_one, l3)))


[-1, 0, 1, 2, 3]

map and filter commands may be nested:


In [45]:
print(list(map(lambda el: el * 2, filter(lambda el: el <= 1, l3))))


[-4, -2, 0, 2]

The map function is very powerful, and makes type conversion very easy:


In [47]:
print('Integer array:', list(map(int, l3)))
print('  Float array:', list(map(float, l3)))
print('Boolean array:', list(map(bool, l3)))


Integer array: [-2, -1, 0, 1, 2]
  Float array: [-2.0, -1.0, 0.0, 1.0, 2.0]
Boolean array: [True, True, False, True, True]

Iterating through multiple lists

The zip command takes as arguments a number of lists, and iterates through these until the shortest list is traversed. This function therefore aligns lists together. Consider the following examples:


In [48]:
l4 = [1, 2, 3]

print('l3:', l3)
print('l4:', l4)


l3: [-2, -1, 0, 1, 2]
l4: [1, 2, 3]

In [49]:
for el3, el4 in zip(l3, l4): 
    print(el3, el4)


-2 1
-1 2
0 3

In [50]:
l5 = l3 + l4
print(l5)


[-2, -1, 0, 1, 2, 1, 2, 3]

In [51]:
for el3, el4, el5 in zip(l3, l4, l5): 
    print(el3, el4, el5)


-2 1 -2
-1 2 -1
0 3 0

Note, if more than one iterable is passed into map.

A simplified implementation of map that takes two iterables is given here

def map_two(func, iterable1, iterable2): 
    out = []
    for iter1, iter2 in zip(iterable1, iterable2): 
        out.append(func(iter1, iter2))
    return out

Note that map copes with 'jagged' iterables in the following way: when iterables are not of same length, map will pad the shorter lists with None. The returned list is therefore always of the same length as the longest iterable.


In [52]:
def add(l, r): 
    try:
        return l * r
    
    except TypeError: 
        # Addition of `None` type is not defined
        return None 

def is_None(l, r): 
    return l is None or r is None

l5 = [5, 4, 3, 2, 1]

print(list(map(add, l4, l5)))
print(list(map(is_None, l4, l5)))


[5, 8, 9]
[False, False, False]

Retreiving the position in the list

The for loops that we have considered iterate over the data elements. It is often convenient to also have access to the position in the sequence during the iteration. The enumerate function provides this:


In [53]:
for index, value in enumerate(l1): 
    print(index, value)


0 1
1 10
2 100

This can also be used in conjunction with the zip function:


In [54]:
for index, (el3, el4, el5) in enumerate(zip(l3, l4, l5)): 
    print(index, (el3, el4, el5))


0 (-2, 1, 5)
1 (-1, 2, 4)
2 (0, 3, 3)

In many applications, it is helpful for the index variable to be offset by a particular value. This can be achieved with the enumerate function by passing an optional argument to the function. In this example we start the indexing with an offset of 100.


In [55]:
for index, (el3, el4, el5) in enumerate(zip(l3, l4, l5), start=100): 
    print(index, (el3, el4, el5))


100 (-2, 1, 5)
101 (-1, 2, 4)
102 (0, 3, 3)

In [ ]:


In [ ]: