Mastering Lists and Tuples in Python
Accessing individual elements of a list by indexing is one of the most fundamental yet powerful operations in Python. Whether you are working with data, building algorithms or automating tasks, mastering this skill will enable you to efficiently manipulate and retrieve specific data points from any list. In this article, we will dive into how list indexing works, exploring practical examples to give you a solid understanding of this essential concept. Let’s unlock the potential of Python lists.
Table of Content
- List and tuple
- Accessing individual elements of a list by indexing
- Getting parts of a list by slicing
- Changing an item in a list by index assignment
- Changing a portion of the list by slice assignment
Lists and Tuples
Lists are ordered collections of items. They can be considered similar to
arrays in other languages. They are more flexible and powerful as they do
not have fixed sizes and can store elements of different types. Lists are the
most commonly used sequence types in Python. Here, are some examples of
list literals.
[27, 13, 14, 26, 19]
Output
[]
['papaya', 'apple', 'banana']
[10, 15, 'black', None, 3.5, True, 15,]
The elements of a list are separated by commas and are enclosed in square
brackets. The first example is a list with five elements and the second
represents an empty list. The elements in a list can be of different types. For
example, the fourth list contains values of type int, str, NoneType,
float and bool. Although lists allow mixed types, they are often used to
store values of the same type. They are commonly used to represent
collections of similar items, such as a list of names or a list of numbers. By
storing values of the same type in a list, we can conveniently perform the
same operation on all the elements of a particular list.
Values in a list need not be unique it can have duplicate values. This means
that the same value can appear multiple times at different positions in the
list. For example, in the fourth list, the value 15 occurs twice.
We can place a trailing comma at the end of the values in a list literal. For
example, in our fourth list literal, we have a comma after the last element,
15, just before the closing square bracket. This trailing comma is ignored and does not cause any syntax error. This can be useful when you want to
add elements to a multi line list or rearrange it while editing your code.
Like integer or string literals, list literals can also be assigned to variables.
list1 = [12, 43, 21, 67, 54, 11]
When this assignment statement executes, Python creates a list object and
makes the name list1 refer to that object.
A list is a referential data structure, which means that it stores references to
its elements. Here, is how we can visualize list1.
The name list1 refers to the list object and the list object stores
references to different objects that represent the elements of the list. So,
although we generally say that a list contains elements, it technically
contains references to those elements.
The list type is mutable; this is the first mutable type that we are discussing.
‘Mutable’ means that an object of type list can be changed, and its
contents can be altered. You can add new elements or delete/overwrite
existing elements from the list object. This is why a list can dynamically
contract or expand at runtime; its size is not fixed. The interpreter
dynamically allocates more memory when required and also dynamically
releases the memory no longer required by the list.
We have discussed some properties of a list. Now, before going further, let
us discuss why we need the list data type. The list type provides a way to
combine related data in order. Let us see an example. Suppose we have this
travel itinerary for a 3 week trip.
1. Delhi 2. Bareilly 3. Srinagar 4. Agra 5. Jaipur 6. Mumbai 7. Goa 8.
Bangalore 9. Kolkata 10. Varanasi
The order of the destination cities is important here. If we need to manage
this trip in our program, then without the list type, we would make ten
variables.
destination1 = 'Delhi'
destination2 = 'Bareilly'
destination3 = 'Srinagar'
destination4 = 'Agra'
destination5 = 'Jaipur'
destination6 = 'Mumbai'
destination7 = 'Goa'
destination8 = 'Bangalore'
destination9 = 'Kolkata'
destination10 = 'Varanasi'
Using a list, we can have all of them in only one data structure and access
them using a single name. Since a list is an ordered data structure, the order
is preserved.
trip = [
'Delhi', 'Bareilly', 'Srinagar', 'Agra',
'Jaipur',
'Mumbai', 'Goa', 'Bangalore', 'Kolkata',
'Varanasi'
]
Now suppose we decide to cut 'Agra’, 'Jaipur’ and 'Mumbai'
from the trip. If we defined 10 variable names, we would have to delete three
variable names. This would create confusion, as now, after the name
destination3, we have the name destination7. In the case of a list,
we can easily delete the items from anywhere inside the list. Similarly, if we have to add more cities to the trip, it would be easier if we use a list.
Suppose you need to include another trip that involves 20 cities. In that case,
you can just make another list instead of defining 20 other names, which is
obviously tedious and difficult to maintain in the program.
When we use a list, we can easily insert new items, delete items, replace
items or reorder them. By using a list, we can group related data under one
name. Structuring the data inside a list also makes it easier to process it
using loops, as discussed in the coming sections.
Strings, lists, tuples and range objects are sequences, as they are ordered
collections of items. All the sequence operations like indexing, slicing,
concatenation and repetition that we have seen for strings are also valid for
lists. However, lists are mutable, so they support other operations that can
make in-place changes. This means that you can make changes in the list
object itself instead of creating a new changed object as we had to do in
strings.
Accessing Individual Element of a List by Indexing
In our program, we can print the whole list by sending the list’s name to the
print function. On the interactive prompt, we can just write the name of
the list and it will be printed. Most of the time, in our program, we would
like to access individual elements of the list.
Similar to strings, the elements of a list can be accessed by writing integer
index values enclosed in square brackets. Like strings, lists also use zero
based indexing. If L is the name of the list, then to access the first element,
we write L[0]; for the second element L[1] and so on. A list of size n has
elements indexed from 0 to n-1. As in strings, we can also give negative
index values to index backward. So, L[-1] represents the last element,
L[-2] the second last element and so on. For a list of length 6, indices
0,1,2,3,4,5 and -1,-2,-3,-4,-5,-6 are valid indices. Any integer less than -1 or
more than 5 will be invalid. If you try to access a list element at an invalid
index, the interpreter will raise an IndexError.
>>> L = [10, 20, 30, 40, 50, 60]
>>> L
Output
[10, 20, 30, 40, 50, 60]
>>> L[1]
Output
20
>>> L[-1]
Output
60
>>> L[10]
Output
IndexError: list index out of range
Getting Parts of a List by Slicing
We can extract a portion of the list by slicing. The slice operations that we
saw for strings work for lists also in the same way. Slicing a list gives us a
part of the list as a new list object. As in strings, we can get a slice of the list
by specifying indices separated by colons inside square brackets. The
detailed syntax of slicing is not repeated here, as it is exactly the same as in
strings.
L = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110]
If the first number inside the square brackets is omitted, it is considered zero, if the second is omitted, it is considered the last index. The third number represents the step and is optional if it is omitted, it is considered 1. The
slice L[:] gives an exact copy of the list, and the slice L[::-1] gives the
reverse of the list.
You can assign these slices to variable names. For example, if you wish to
make a list L1 that is the reverse of the list L, you can write this.
L1 = L[::-1]
This statement will make L2 a copy of the list L.
L2 = L[:]
Changing an Item in a List by Index Assignment
Since lists are mutable, it is possible to change a list object in-place. We can
change any element in the list by assigning it to an index. In this
example, we are changing the element at index 1.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[1] = 50
>>> L
Output
[12, 50, 21, 67, 54, 11, 13]
If the index is out of range then an IndexError will be raised.
>>> L[7] = 100
Output
IndexError: list assignment index out of range
Python performs bounds checking while indexing so accessing or assigning
off the end of a list is an error. The statement L[7] = 100 will not just
silently grow the list; instead, it throws an error. There are specific methods
to grow a list, which we will see in a while.
Changing a Portion of the List by Slicing Assignment
You can modify portions of the list by assigning them to slices. When a list
slice is used on the left side of an assignment, the range specified in the slice
will be replaced by what is on the right-hand side. Suppose we have this list.
>>> L = [12, 43, 21, 67, 54, 11, 13]
This assignment statement replaces the elements at index 2, 3, and
4 with the three elements of the list on the right side.
>>> L[2:5] = [300, 400, 500]
>>> L
Output
[12, 43, 300, 400, 500, 11, 13]
Slice assignment can replace multiple elements of the list in a single step.
The length of the list on the right side need not be equal to the length of the
slice that is being assigned.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[2:5] = ['a', 'b', 'c', 'd', 'e']
>>> L
Output
[12, 43, 'a', 'b', 'c', 'd', 'e', 11, 13]
In this example, the length of the slice is three while five items are being
assigned and we can see that all five elements are included in the resultant
list. So, the length of the slice and the length of the list that is being assigned
need not be the same, the list will shrink or expand to accommodate the new
values. This flexibility only exists if you do not provide a step in the slice.
When the slice includes a step, the lengths of the slice being assigned to and the length of the list on the right side should be the same. If the step is not
provided, their lengths can be different.
In our example, we have used a list on the right side of an assignment
statement. You can use any other iterable; for example, you can have a string
or a tuple also on the right side. Let us use a string.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[3:6] = 'abcdef'
>>> L
Output
[12, 43, 21, 'a', 'b', 'c', 'd', 'e', 'f', 13]
Now, the specified portion of the list is occupied by characters of the string
on the right side.
We can delete a portion of the list by assigning an empty list to a slice.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[3:6] = []
>>> L
Output
[12, 43, 21, 13]
Here, all the elements from index 3 to index 5 are deleted from the list.
We know that the slice L[:] represents the whole list, so assigning to it will
replace the whole list with the list on the right side.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[:] = [1, 2, 3, 4]
>>> L
Output
[1, 2, 3, 4]
If you want to clear the whole list, you can write this.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[:] = []
>>> L
Output
[]
We can insert multiple new elements in a list by squeezing them into an
empty slice at the desired location.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[3:3] = [10, 20, 30, 40]
>>> L
Output
[12, 43, 21, 10, 20, 30, 40, 67, 54, 11, 13]
The four elements of the list on the right side are inserted in the list L
starting at index 3. This way, you can insert new elements without deleting
any existing ones.
If you want to add some items to the beginning of the list, you can write this.
>>> L = [12, 43, 21, 67, 54, 11, 13]
>>> L[0:0] = [7, 8, 9] # or L[:0] = [7, 8, 9]
>>> L
Output
[7, 8, 9, 12, 43, 21, 67, 54, 11, 13]
When assigning to slices, there should always be an iterable on the right
side even if it contains zero or no elements.
>>> L[5:5] = 90
Output
TypeError: can only assign an iterable
>>> L[5:5] = [90]
>>> L
Output
[7, 8, 9, 12, 43, 90, 21, 67, 54, 11, 13]
In strings, index assignments and slice assignments are not possible as they
are immutable. List objects are mutable, so they can be changed in-place;
hence, index and slice assignments are allowed.
These slice assignments are not commonly used in practice as there are
specific list methods for performing most insertion and deletion operations.
The names of these methods are self explanatory hence, using them is simpler than using slice assignments in most cases. In the few
sections, we will explore these methods.
Adding an Item at the End of the List by Using Append()
The append() method adds a single item at the end of the list and it
returns None.
>>> numbers = [10, 20, 30, 40]
>>> numbers.append(50)
>>> numbers
Output
[10, 20, 30, 40, 50]
We have taken a list of four integers and added another integer to it at the
end using the append method.
Conclusion
Lists and tuples are fundamental data structures in Python that play critical roles in managing collections of data. While lists offer flexibility with their mutability, allowing you to modify, add and remove elements, tuples provide an extra layer of security with their immutability, making them perfect for situations where data integrity is paramount. Understanding when to use each structure is key to writing efficient, clean and reliable Python code. By mastering lists and tuples, you are well on your way to unlocking Python’s full potential in handling complex data structures with ease and precision. Keep experimenting, keep coding and let these structures empower your programming journey.
Code bundle here:
https://github.com/codeaihub1998/Ultimate_Python_1-to-21
If you read this article till the end, please consider the following:
- Follow the author to get updates of upcoming articles 🔔
- If you like this article, please consider a clap 👏🏻
- Highlight that inspired you
- If you have any questions regarding this article, please comment your precious thoughts 💭
- Share this article by showing your love and support ❤️