Skip to content

Lists

We've already seen lists in the datatypes lesson. Lets learn more about them.

As you already know, the len function can be used to find the length of a list.

x = ["a", "b", "c", "d"]
print(x)
print(len(x))

Individual elements of a list can be accessed using the [] operator. The indexing starts with 0. It is also possible modify the elements of a list by assigning a new value to them.

x = ["a", "b", "c", "d"]
print(x[0], x[1])

x[0] = 'aa'
print(x)

For loop

The for loop is used to iterate over a list of elements.

names = ["Alice", "Bob", "Charlie", "Dave"]

for name in names:
    print("Hello", name)

Please note that the body of the for loop is indented.

If we use the for loop in a function, then the loop itself will be indented to place it inside the function and the body of the for loop will be indented by one more level.

def welcome_all(names):
    for name in names:
        print("Hello", name)

names = ["Alice", "Bob", "Charlie", "Dave"]
welcome_all(names)

Let's take up a small problem. How to print words of sentence, each in a seperate line?

sentence = "when in doubt, use brute force"

words = sentence.split()
# print(words)

for word in words:
    print(word)

We can also combine the split and the loop into a single line.

sentence = "when in doubt, use brute force"
for word in sentence.split():
    print(word)

Example: sum

Python has a built-in function sum.

a = sum([1, 2, 3, 4])
b = sum(range(10))
c = sum(range(100))

print(a, b, c)

Let's try to implement our own sum function.

def my_sum(numbers):
    # initialize the result to the initial value
    result = 0

    # update the result in every iteration of the loop
    for n in numbers:
        result = result + n

    # finally, return the result
    return result


a = my_sum([1, 2, 3, 4])
b = my_sum(range(10))
c = my_sum(range(100))

print(a, b, c)

The range function

The range function is used to create a sequence of numbers.

For example, range(5) creates 5 numbers starting from 0. That includes 0, 1, 2, 3 and 4. Please note that the number 5 is not included in it.

We can use the for loop to iterate over a range.

for i in range(5):
    print(i)

We can use the list function to quickly see the elements of a range.

The range function can also be used with 2 or 3 arguments as shown in the example below.

# numbers from 0 to 5 (last is not included)
print(list(range(5)))

# number from 2 to 5
print(list(range(2, 5)))

 # number from 2 to 20 in steps of 3
print(list(range(2, 20, 3)))

If we want to print a message n times, we can do that using a range.

for i in range(3):
    print("hello")

Growing Lists

Let's see how to grow a list by adding new elements to it.

Lists have a method append that add a new element to a list at the end.

x = ['a', 'b', 'c']
print(x)

x.append('d')
print(x)

x.append('e')
print(x)

Example: squares

Let's write a function squares, that takes a list of numbers as argument and computes squares of each one of them.

Here is what we expect when we run the square function.

>>> squares([1, 2, 3, 4])
[1, 4, 9, 16]
>>> squares([2, 3])
[4, 9]

Let's see how to implement it.

def squares(numbers):
    result = []
    for n in numbers:
        result.append(n*n)
    return result

a = squares([1, 2, 3, 4])
print(a)

b = squares([2, 3])
print(b)

c = squares(range(10))
print(c)

# sum of squares of all integers below one million
d = sum(squares(range(1000000)))
print(d)

List Comprehensions

List comprehensions are very expressive way to transform a list into another.

numbers = [1, 2, 3, 4, 5, 6]

a = [n*n for n in numbers]
print(a)

b = [n*n for n in numbers if n %2 == 0]
print(b)

Let's try to understand the pattern clearly:

[expr for a_var in a_list]
[expr for a_var in a_list if some_condition]

Let's see the same as for loop:

result = []
for a_var in a_list:
    result.append(expr)

result = []
for a_var in a_list:
    if some_condition:
        result.append(expr)

How to find all the python files in the current directory?

import os
files = [f for f in os.listdir(".") if f.endswith(".py")]
print(files)

How to find the total size of all the python files in the current directory?

import os
size = sum([os.path.getsize(f)
            for f in os.listdir(".")
            if f.endswith(".py")])
print(size)

Iteration Patterns

Let's review the common iteration patterns used in Python.

Iterating over a list

The most commonly used iterating pattern is iterating over a list of values.

x = ['a', 'b', 'c', 'd']

for a in x:
    print(a, a.upper())

We can also use this pattern in a list comprehension.

x = ['a', 'b', 'c', 'd']
y = [a.upper() for a in x]
print(y)

Iterate over a sequence of numbers

The range function is used whenever we need to iterate over a sequence of numnbers.

for i in range(5):
    print(i, i*i)

And here is the list comprehension version of the same pattern.

squares = [i*i for i in range(5)]
print(squares)

Iterating over two lists together

Sometimes we need to iterate over two lists together, and the built-in function zip takes care of that.

fruits = ['Apple', 'Banana', 'Mango', 'Orange']
prices = [10, 20, 30, 40]

for fruit, price in zip(fruits, prices):
        print(fruit, price)

Iterating over the index and the elements together

Sometimes we need to the index of the element we are accessing. Python covered you there too using the built-in function enumerate.

For example, consider the following program where we are trying to print the index and the title of every chapter in a book.

chapters = [
    "Getting Started",
    "Functions",
    "Lists",
    "Dictionaries"
]

for i, title in enumerate(chapters):
    print(f"Chapter {i+1}: {title}")

List Indexing

We've already seen how to index a list, but there is more to indexing.

The common way to index a list using the index starting with 0.

x = ['a', 'b', 'c', 'd']
print(x[0])
print(x[1])

Python also supports indexing from the other end using negative indexes.

+----+----+----+----+
|  a |  b |  c |  d | list
+----+----+----+----+
|  0 |  1 |  2 |  3 | --> regular index
+----+----+----+----+
| -4 | -3 | -2 | -1 | <-- negative index
+----+----+----+----+
x = ['a', 'b', 'c', 'd']
print(x[-1]) # last element
print(x[-2]) # second last element

We could use this to find the last element of a sentence.

def get_last_word(sentence):
    return sentence.split()[-1]

w = get_last_word("one two three")
print(w)

List Slicing

List slicing allows us to create a slice or part of a list very eaily.

x = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

print("x -", x)

# from index 0 unto index 2 (end is not included)
print("x[0:2] -", x[0:2])

# upto index 2
print("x[:2] -", x[:2])

# from index 1 upto 6
print("x[1:6] -", x[1:6])

# from index 2 onwards
print("x[2:] -", x[2:])

# every alternative element, from index 0 to 6th in steps of 2
print("x[:6:2] -", x[:6:2])

# from index 1 upto 6, take every 2nd element
print("x[1:6:2] -", x[1:6:2])

# all except the last element
print("x[:-1] -", x[:-1])

# How to get the last two elements?
print("x[:-2] -", x[:-2])

# copy of x
print("x[:] -", x[:])

# how to get the list in reverse order?
print("x[::-1] -", x[::-1])

Sorting Lists

Python makes it very easy to sort lists.

The sort method on lists, sorts the list in-place.

names = ["alice", "dave", "charlie", "bob"]
names.sort() # sorts in-place
print(names)

The built-in function sorted, returns a new sorted list.

names = ["alice", "dave", "charlie", "bob"]

names2 = sorted(names)
print(names2)

# The names list would remain unchanged
print(names)

Just like how we used the key argument for min and max we could use that with the sorted function or the sort method.

The following example, sorts the names by their length.

names = ["alice", "dave", "charlie", "bob"]
names2 = sorted(names, key=len)
print(names2)

We can pass optional argument reverse=True to sort it in the reverse order.

names = ["alice", "dave", "charlie", "bob"]
names2 = sorted(names, reverse=True)
print(names2)