Ensure that you are familiar with lesson 9 and the text (ch8) for lists and also with the section on strings. Another useful resource is this page which includes some additional information and an example of encryption.
The list object in python is a collection that can store different object types. Indexing and iteration through a list works exactly the same as with strings.
Lists are a MUTABLE data structure.
The list builtin function can be applied to other object types:
>>> word = 'hello'
>>> print(list(word))
['h', 'e', 'l', 'l', 'o']
>>> my_tup = (200, 147, 255)
>>> list(my_tup)
[200, 147, 255]
Indexing in a list works just like with strings, but unlike strings which are composed of chars, a list may be composed of other iterables. This allows us to index into iterables within a list, and even into the iterables within the iterables within the list:
>>> my_list = [1, 5.2, 96, "FACE", "Python", 8.6, ['a', 'b', 3, 'c'], 333]
>>> my_list[-1]
333
>>> my_list[-2][0]
'a'
>>> my_list[3][-1]
'E'
>>> my_list[-2][-1] = 'cat' # modify a value => mutable
>>> my_list
[1, 5.2, 96, 'FACE', 'Python', 8.6, ['a', 'b', 3, 'cat'], 333]
>>> my_list[-2][-1][1]
'a'
>>> my_list[-20]
Traceback (most recent call last):
File "<pyshell>", line 1, in <module>
IndexError: list index out of range
a list of lists can be used to create a 3D type structure
In addition to the preceding parts :D
List support two operators: + for concatenation and * for repeating the elements.
>>> ls_1 = [1]
>>> ls_2 = [2, 3]
>>> ls_3 = [4, "5", (6, 7)]
>>> ls_1 + ls_2 + ls_3
[1, 2, 3, 4, '5', (6, 7)]
>>> lst_1 = [1, 2]
>>> lst_1 * 3
[1, 2, 1, 2, 1, 2]
We can slice a list to create another list from its elements.
>>> my_list = [1, 5.2, 96, "FACE", "Python", 8.6, ['a', 'b', 3, 'c'], 333]
>>> my_list[2 : 6]
[96, 'FACE', 'Python', 8.6]
We can iterate over list elements using the for loop.
We can use “in” operator to check if an item is present in the list or not. We can also use “not in” operator with a list.
A list is used to store homogeneous elements where we want to mutate - add/update/delete - elements.
There are many ways to concatenate lists- let's look at a few:
The '+' operator can be used to concatenate two lists. It appends one list at the end of the other list and results in a new list as output. This is a common way to combine lists in Python.
list1 = [10, 11, 12, 13, 14]
list2 = [20, 30, 42]
res = list1 + list2
print ("Concatenated list:\n" + str(res))
Output:
Concatenated list:
[10, 11, 12, 13, 14, 20, 30, 42]
Best for: Teaching/basic control flow or when you must append with extra per-item logic.
In the Naive method, a for loop is used to traverse the second list. During the traversal, the elements from the second list are appended to the first list. The first list results out to be the concatenation of the first and the second list.
list1 = [10, 11, 12, 13, 14]
list2 = [20, 30, 42]
print("List1 before Concatenation:\n" + str(list1))
for x in list2 :
list1.append(x)
print ("Concatenated list i.e. list1 after concatenation:\n" + str(list1))
Output:
List1 before Concatenation:
[10, 11, 12, 13, 14]
Concatenated list i.e. list1 after concatenation:
[10, 11, 12, 13, 14, 20, 30, 42]
Best for: Creating a new list with filtering or transformation while concatenating. Python List Comprehension is an alternative method to concatenate two lists in Python. List Comprehension is basically the process of building/generating a list of elements based on an existing list. This is a useful python concatenate list of lists technique. It uses for loop to process and traverses the list in an element-wise fashion. The below inline for-loop is equivalent to a nested for loop.
list1 = [10, 11, 12, 13, 14]
list2 = [20, 30, 42]
res = [j for i in [list1, list2] for j in i]
print ("Concatenated list:\n"+ str(res))
Output:
Concatenated list:
[10, 11, 12, 13, 14, 20, 30, 42]
Best for: In-place updates to an existing list with minimal memory overhead. Python’s extend() method can be used to concatenate two lists in Python. The extend() function does iterate over the passed parameter and adds the item to the list thus, extending the list in a linear fashion.
Syntax: list.extend(iterable)
list1 = [10, 11, 12, 13, 14]
list2 = [20, 30, 42]
print("list1 before concatenation:\n" + str(list1))
list1.extend(list2)
print ("Concatenated list i.e list1 after concatenation:\n"+ str(list1))
All the elements of the list2 get appended to list1 and thus the list1 gets updated and results as output.
Output:
list1 before concatenation:
[10, 11, 12, 13, 14]
Concatenated list i.e list1 after concatenation:
[10, 11, 12, 13, 14, 20, 30, 42]
Best for: Combining multiple lists in one expression with maximal readability. Python’s '*' operator can be used to easily concatenate two lists in Python. The ‘*’ operator in Python basically unpacks the collection of items at the index arguments.
For example: Consider a list:
my_list = [1, 2, 3, 4]
The statement *my_list would replace the list with its elements at the index positions. Thus, it unpacks the items of the lists.
>>> print(*my_list)
1 2 3 4
Example for concatentation
list1 = [10, 11, 12, 13, 14]
list2 = [20, 30, 42]
res = [*list1, *list2]
print ("Concatenated list:\n " + str(res))
In the above snippet of code, the statement res = [*list1, *list2] replaces the list1 and list2 with the items in the given order i.e. elements of list1 after elements of list2. This performs concatenation and results in the below output.
Concatenated list:
[10, 11, 12, 13, 14, 20, 30, 42]
You've just seen two very commonly used list methods-
We can use the method extend to add new elements to the list:
# Use extend to add elements to list
>>> arr = [ "The Bodyguard", 7.0]
>>> arr.extend(['pop', 10])
>>> arr
['The Bodyguard', 7.0, 'pop', 10]
Another similar method is append. If we apply append instead of extend, we add one element to the list:
# Use append to add elements to list
>>> arr = [ "The Bodyguard", 7.0]
>>> arr.append(['pop', 10])
>>> arr
['The Bodyguard', 7.0, ['pop', 10]]
Each time we apply a method, the list changes. If we apply extend we add two new elements to the list. The list L is then modified by adding two new elements:
# Use extend to add elements to list
>>> arr = [ "The Bodyguard", 7.0]
>>> arr.extend(['pop', 10])
>>> arr
['The Bodyguard', 7.0, 'pop', 10]
If we append the list ['a','b'] we have one new element consisting of a nested list:
# Use append to add elements to list
>>> arr.append(['a','b'])
>>> arr
['The Bodyguard', 7.0, 'pop', 10, ['a', 'b']]
As lists are mutable, we can change them. For example, we can change the first element as follows:
# Change the element based on the index
A = ["disco", 10, 1.2]
print('Before change:', A)
A[0] = 'hard rock'
print('After change:', A)
Results in:
Before change: ['disco', 10, 1.2]
After change: ['hard rock', 10, 1.2]
We can also delete an element of a list using the del command:
Here is a list (pun) of some of the most commonly used list methods, and functions you will be familiar with such as the len function (works on all collections).
Delete the element based on the index
print('Before change:', A)
del(A[0])
print('After change:', A)
Results in:
Before change: ['hard rock', 10, 1.2]
After change: [10, 1.2]
We can convert a string to a list using split. For example, the string method split translates every group of characters separated by a space into an element in a list:
# Split the string, default is by space
>>> 'hard rock'.split()
['hard', 'rock']
We can use the split function to separate strings on a specific character which we call a delimiter. We pass the character we would like to split on into the argument, which in this case is a comma. The result is a list, and each element corresponds to a set of characters that have been separated by a comma:
# Split the string by comma
>>> 'A,B,C,D'.split(',')
['A', 'B', 'C', 'D']
When we set one variable B equal to A, both A and B are referencing the same list in memory:
# Copy (copy by reference) the list A
A = ["hard rock", 10, 1.2]
B = A
print('A:', A)
print('B:', B)
results in:
A: ['hard rock', 10, 1.2]
B: ['hard rock', 10, 1.2]
Initially, the value of the first element in B is set as "hard rock". If we change the first element in A to "banana", we get an unexpected side effect. As A and B are referencing the same list, if we change list A, then list B also changes. If we check the first element of B we get "banana" instead of "hard rock":
# Examine the copy by reference
print('B[0]:', B[0])
A[0] = "banana"
print('B[0]:', B[0])
results in:
B[0]: hard rock
B[0]: banana
We can clone list A using either of the following methods:
# Clone (clone by value) or using the copy method
>>> A = ["soft rock", 10, 1.2]
>>> B = A[:]
>>> B[0] = "Jazz"
>>> C = A.copy()
>>> C[-1] = 3.14
>>> print("A is now: ", str(A))
>>> print("B is now: ", str(B))
>>> print("C is now: ", str(C))
results in:
A is now: ['soft rock', 10, 1.2]
B is now: ['jazz', 10, 1.2]
C is now: ['soft rock', 10, 3.14]
There are many additional methods that we can make use of when working with lists:
We can use a loop to read a list, or to write a list. In reading a list, the len of the list will be definite, so a for loop is an appropriate form. If creating a list of indefinite length, we may use a while loop, and append into the list until a condition triggers an end to the loop.
reading a list using a for loop with the range function
The code below will read a list of multiline str and loop through each one with a one second delay between images.
Images are ascii characters. The cls() function clears the screen between displaying each character.
While the above code uses a range in order to print the index as well as the value squared, we could omit the range if we did not want the index, or we could have used a while loop instead.
This may seem trivial, but is in fact very important to understand- when a list is passed to a function, the function receives a reference to the actual list- NOT A COPY.
str: passes to function by value (a copy)
By contrast:
list: passes to function by reference
This can be a source of huge confusion when working with lists, as one may think they are working with a copy but are in fact working with the original source list. Any mutating (append, pop, remove, delete, addition etc) that happens in the function (local scope) has an effect on the list in global scope.
The second time that the function is called, it receives a copy of the list, or perhaps more accurately, a slice of all the values that the list contains. When we print a slice of a list, does it change the list or make a copy of the contents to display for us? It is the same logic here- the slice takes a copy of everything in the list, sends it to the function, the function appends on values, and returns this mutated list. The mutated list is printed and then discarded as it is not assigned to any variable and is thus lost to be picked up by the garbage collection service.