All over programming languages, working with arrays is more than just a common task. This is because a ton of algorithms on sorting, searching, graphs, trees can all be based on arrays.
Even on an elementary level, it's not surprising to save a list of numbers, a list of strings, a list of food items in an array. The array holds all the data under one single location and provides facilities to access each item.
To boil it all down, arrays play a paramount role in computer programming.
As far as Python is concerned, it also provides arrays at the dispense of developers. However, it doesn't call them 'arrays' — rather it refers to them as lists.
In Python, a list is a sequential data type. Specifically:
List indexes work similar to string indexes. The
nth item of the list is at index
n - 1.
For example, the first item is at index
0, the second is as index
1, the third is at index
2, and so on and so forth.
Creating a list
To create a list in Python, we use a pair of square brackets and put the individual items of the list inside it. Different items are separated by a comma.
nums = [10, 20, 30]
Here we've created a list containing three items —
30 — and assigned it to the variable
Once a list is created, it's rare to never ever use it.
To access an item in a list, we start by writing the identifier holding the list, followed by a pair of square brackets. Inside these, we put the index of the item we wish to retrieve.
Consider the code below:
nums = [10, 20, 30] print(nums)
We access the first item of
nums and print it. Note the index
0 inside the square brackets — it's the index of the first item.
To change the value of a list item, just use the same bracket syntax as shown above, followed by the
= assignment operator, followed by the new value you wish to change the item to.
Below we change the first item of
nums = [1, 2, 3, 4, 5] print(nums) nums = 10 print(nums)
To get the total number of items in a list, use the
len() function, just like it's used on strings.
nums = [10, 20, 30] print(len(nums))
To remove an item from a list, we have two options. Either use the
remove() method or the
del keyword. Let's first explore
If called on a list item, the
del keyword deletes the item from the list, shifting down items if the need be.
To delete an item at index
i from a list
l, we call
In the following code we delete the third item from
nums = [10, 20, 30] del nums print(nums)
Since the third item was the last item of
nums, deleting it didn't require other items to be shifted down by one index.
This shifting is required in places where there are items following the deleted item, such as in the code below:
nums = [10, 20, 30] del nums print(nums)
Here we are deleting the first item from
nums. Since more items follow it, when it's deleted, all the following items are shifted one place back. The item at index
1 comes at index
0, the one at index
2 comes at index
1 and so on.
If this hadn't been done, deleting items using
del would have left empty holes in lists, which sounds senseless!
Lists in Python, unlike arrays in languages such as Java, C#, are loosely typed. That is, there is no restriction at all on the type of values that can be stored inside a list.
A list can have elements of literally any type.
Below we create a list
values holding five different types of values: a string, an integer, a float, a Boolean, and a tuple:
values = ["Hello", 10, 3.5, True, (1, 3)]
This diversity that Python provides in lists is both good and bad in one way. The good thing is that it gives developers a lot of freedom over constructing lists.
The bad thing is that due to this loosely typed nature of lists, a lot of overhead is stored for each item, even if all the items are of the same type.
For strictly-typed lists, where each item has the same type, we have the
array module which we shall explore later in this unit.
Adding new elements
Adding new elements to a list is possible in Python unlike some languages such as C++, C#, Java, etc. This is referred to as variable-width lists.
There are mainly two ways to add an element to a list in Python — either use the
append() method or use the
Both of these methods are demonstrated below:
append() method takes an argument and adds it to the end of a given list.
In the code below, we add the number
40 to the list
nums = [10, 20, 30] nums.append(40) print(nums)
Note that it's not possible to add multiple elements to a list using a single call to
append, as is otherwise possible in some other languages:
nums = [10, 20, 30] nums.append(40, 50, 60)
Also note that it's necessary to provide an argument to
append() — without one, it would throw an error.
Take a look at the code below:
nums = [10, 20, 30] nums.append()
append() without an argument in line 2, and likewise get an error thrown.
'Push' means 'append' in Python
A much more common term used to refer to the action of adding an element to the end of a list (or array) is 'push'. Most programming languages use this name in methods or functions that do the same job as
append() in Python.
Python just like to take a new dimension by using the name 'append'.
The second way to add an element to a list is to use the
It works similar to
append() in that it also takes as argument the value to be added to the list. However, this argument is not the first one. The first argument specifies the index where to add the value, passed as the second argument.
insert() doesn't perform any replacements. It always adds an item at a given position, shifting all the elements beyond that position one step forwards.
Below we add
40 at the start of
nums = [10, 20, 30] nums.insert(0, 40) print(nums)
40 gets added to
nums at index
10, which was previously accessible at this index, is now accessible at the next index i.e
1. The same goes for
Similarly, in the code below we add
40 at index
1 in the same list
nums = [10, 20, 30] nums.append(1, 40) print(nums)
Searching for elements
It's a common task to search for something within a list. In Python, this can be done using the
The general syntax of
in is shown below:
value in iterable
The left operand states the value to search for in the given
iterable, which is given as the right operand. A Boolean value is returned indicating whether or not the value exists in the iterable.
In this case,
iterable would be a list.
A couple of examples using the
in keyword are shown as follows:
nums = [10, 20, 30] print(30 in nums) print('30' in nums)
30 exists in
30 in nums evaluates to
'30' doesn't exist in the list and consequently,
'30' in nums returns
Following we check for
'LONDON' in a list of cities:
cities = ['London', 'Paris', 'Berlin'] print('London' in cities) print('LONDON' in cities)
Keep in mind, that the comparison made by
in is using the
== operator. That is, for each element
item, the expression
value == item is evaluated. If it returns
True, this implies that a match is found, ultimately resolving the
in operation by
If all elements of the list are exhausted and no matches are found, the
in operation evaluates to
This type of searching is known as linear search, or sequential search.
To make sure that a certain value doesn't exist in a list, we can use the same
in operator along with the
not in returns
True if the given value is not in the list, or else
False. It's simply a negation of the
nums = [10, 20, 30] print(30 not in nums) print('30' not in nums)
Checking for an empty list
In Python, an empty list is considered a falsey value. This means that when coerced into a Boolean, an empty list would convert to
Similarly, a non-empty list coerces to
This idea is useful when we want to add elements to a list only if it is empty. Consider the code below.
nums =  if not nums: # add a 0 to nums, if it's empty nums.append(0)
First we check whether the list
nums is empty. If it is, we append a
0 to it. The expression
not nums returns
nums coerces to
False — which happens when it's empty.
Although the example shown above is not really practical, it does demonstrate how to check for an empty list, using just the list itself.
If you think on it, there is one more way to do this. That is using the
len() function. To check if a list is empty, we simply check if its length is equal to
If it is, we know that the list has no elements.
nums =  if len(nums) == 0: # add a 0 to nums, if it's empty nums.append(0)
Use whichever way suits you; sometimes the coercion method works well if we read the code out loud. Similarly, sometimes it's the length-check method that seems the best choice.
One strange behavior of Python, that developers learning the language face is shown below:
l1 = [1, 3, 5] l2 = l1 # make a change in l1 l1 = 10 print(l2)
First a list is assigned to the variable
l1 is assigned to another variable
l2. Now a modification is made in
Ideally, we've only modified
l1 and so want the change to happen in it, only. However, what happens is that both
l2 are found changed; not just
What's this? Is this some kind of an error?
Well, first of all this is perfectly normal behavior arising from the fact that lists in Python are stored by references.
Let's see what's the big idea?
That is, a variable holding a list doesn't hold the actual data of the list. Rather, it holds a reference to a location in memory where the list's data resides.
We'll see why is this approach taken for lists, but first let's resolve the confusion in the code above.
l1 = [1, 3, 5] l2 = l1 # make a change in l1 l1 = 10 print(l2)
We create a list
[1, 3, 5] and put it in the variable
l1. Python creates this list in memory, and then saves its location inside the variable
l1 holds the reference to the list
[1, 3, 5] — NOT the actual list.
Next up, we assign
l1 to a new variable
l1 holds a reference,
l2 will get to hold this very reference. At this stage, both
l2 hold the same reference.
Now, we change something in the list
l1. These changes are made exactly where
l1 points to. That is, the changes are made in the list
[1, 3, 5] stored in memory.
Next up, accessing
l2 returns this modified list, since
l2 holds a reference to this same list.
The other way round, if we change something in the list using the variable
l2, the changes would be visible in
l1, as well. Why?
Simply because both variables
l2 hold references to the exact same location in memory, where the list
[1, 3, 5] resides.
Now the question is what's the point of giving this feature.
Why use references?
If this reference mechanism wasn't built into Python, assigning a list variable to another variable would have ended up creating a copy of the list. Passing a list to a function as an argument would've also ended up creating a new list even if the job of the function was to only run through the list.
To boil it down, there would've been so many useless copies of lists in memory, that the program's performance would've been compromised to a considerable extent on machines with already less amounts of memory.
The solution: create a list once, and pass on its references.
One interesting question that comes up at this point is that what if a copy is desired. What if someone does want to make a new list similar to one in memory?
Fortunately, there are ways.
For simple 1D lists, copies can be made by slicing the lists. For deeper 2D or 3D lists, copies can be made by means of the
To make a copy of a simple 1D list, as shown below, slice the list from its start to its end:
l1 = [1, 3, 5] l2 = l1[:] # make a copy of l1
When a list is sliced, what happens is that every element of the list is put inside a new list.
Simple cloning cases can be solved just by using list slices. Other complex cases need complex methods.
An example is shown below:
matrix = [[1, 5], [7, 1], [0, 0]]
Here we have a 3 x 2 matrix, represented using a list of lists. To truly copy this list, slicing is not one of the candidates.
matrix[:] surely returns a copy of
m. However, every element of this new list is the same element of
matrix, all of which are lists themselves. That is, if we make a change in one of the elements of
m, that would be seen in the same element in the copied list.
This is because
matrix[:] merely assigns each element of
matrix to the corresponding element in the new list. And since these elements are lists themselves, they get passed as references.
To copy deeper lists as well, we ought to use the
deepcopy() function of the
This can be seen below:
import copy matrix = [[1, 5], [7, 1], [0, 0]] matrix2 = copy.deepcopy(matrix)
The function copies every list is encounters while running over a main list. The result is a truly copied list, having no sort of attachment with the original list.
deepcopy()is an expensive operation. So use it only when you really want a true copy of a complex list!
Apart from denoting lists literally, we can create them using the
It works by taking an iterable sequence and putting each of its elements in a list, finally returning the list.
For instance, if we call
list() on a string, we will get a list of all its characters, since a string is a sequence of characters and
list() takes each item of a sequence and makes it an element of the newly-created list.
Consider the code below:
s = 'Hello' print(list(s))
list() on strings is a common routine in Python programs when each character of a string needs to be modified and processed separately.
Below we split a string of space-delimited numbers into a list, and then work with each element of the list separately:
nums = '10 11 12' nums_list = nums.split() for num in nums_list: if int(num) % 3 == 0: print('Yes') else: print('No')
nums_list is a list of stringified numbers —
['10', '20', '30']. In the
for loop, we retrieve each of these strings, convert it to an integer, and then check if it is divisible by
If it is divisible,
'Yes' is printed, or else
'No' is printed.
Immutable in nature
Recall from the Python String Basics chapter, that strings in Python are immutable in nature. That is, a string, once created, can't be changed.
Lists are the opposite of this — they are immutable in nature.
Once a list is created, it can be altered. This can happen by adding new elements, changing existing elements, and by deleting elements.
We've seen how to perform all three of these operations in the discussion above. When we perform either of these operations on a list, we are in effect mutating the list.
Mutating simply means that we are changing the list. Hence, we say that lists are mutable.
Let's review all of them once again: