Lists
The List Data Structure
A data structure contains data that are more complex than a single data value such as an integer. Lists are one such very useful data structure used in Python. A list contains an ordered sequence of items. Python uses square brackets to indicate lists.
Some list examples:
friends = [] # an empty list
fruits = ['apple', 'banana', 'orange'] # a list containing 3 string items
values = [0, 3.4, 'High', True] # a list containing items of different types
print(friends, fruits, values)
everything = [friends, fruits, values] # a list containing other lists
print(everything)
[] ['apple', 'banana', 'orange'] [0, 3.4, 'High', True]
[[], ['apple', 'banana', 'orange'], [0, 3.4, 'High', True]]
You can use the notation list_name[index_of_item]
to access an item of a list. List indexes start from 0
i.e., the first item in a list has the index 0
.
The code below shows how to use indexes to access items in a list.
| → |
|
As you can see from the above example, list indexes can be negative; index -1
refers to the last item in the list, -2
refers to the second last item in the list, and so on.
The code below shows how to use indexes to update items in a list.
| → |
|
The example below shows how to access an item of a list that is inside another list; list_of_lists[2]
accesses list_of_lists[2]
which gives you the item at index 2 ([0.1, 0.2]
) which is also a list, and then accesses the item at index 0
from that list, which is 0.1
.
| → |
|
Object References
Objects of some Python types are immutable. An immutable object, once created, cannot be modified.
- Some immutable types you have seen already:
int
,float
,decimal
,bool
,str
- Other immutable types:
complex
,tuple
,range
,frozenset
,bytes
Objects of some other Python types are mutable. A mutable object can be modified during its lifetime.
- Some mutable types:
list
,dict
,set
,bytearray
In the example below, i = 5
assigns an integer to i
. Because integers are immutable, i = i + 1
is not mutating the integer object 5
; rather, it is simply creating a new integer object 6
and assigning that to i
.
Similarly, s = s + ' World!'
is not adding more letters to the existing string object Hello
; rather, it is creating a new string object 'Hello World!'
and assigning that to s
.
i = 5
i = i + 1 # i is assigned a new integer object 6
s = 'Hello'
s = s + ' World!' # s is assigned a new string objet 'Hello World!'
numbers = [1, 2, 3]
numbers[0] = 4 # the existing list is being modified
However, lists are mutable. Therefore, numbers[0] = 4
is changing the existing list [1, 2, 3]
rather than creating a new list object.
An object reference is the address of the memory location where an object is currently stored. A variable is a name that is bound to an object reference. We can think of it as the variable pointing to an object that is stored at the object reference.
The variable spam
is bound to the object reference 57207444
which is the memory location of the list object [0, 1, 2, 3, 4, 5]
. i.e., spam
is pointing to the object [0, 1, 2, 3, 4, 5]
using the object reference 57207444
.
spam = [0, 1, 2, 3, 4, 5]
Image credit: AtBSwP
When you assign variables as v1 = v2
, if v2
is pointing to a mutable object, its object reference is copied to v1
so that now both v1
and v2
are pointing to the same object. However, if v2
is pointing to an immutable object, v1 = v2
results in each variable pointing to its own copy of the object.
Continuing with the previous example, cheese = spam
results in the object reference stored in spam
being copied into the variable cheese
. The end result is both variables are now pointing to the same list object.
cheese = spam
Image credit: AtBSwP
This means any change to the list pointed by cheese
will be reflected as a change to the list pointed to by spam
(because both are pointing to the same list object!).
| → |
|
Image credit: AtBSwP
However, the behavior is different when we do a similar variable assignment and update using integers instead of lists. Because integers are immutable, cheese
points to a copy of the integer pointed to by spam
, not the same object.
| → |
|
The behavior for passing arguments to a function is similar to that of assigning one variable to another. If the argument is mutable, its object reference is assigned to the parameter. That means the code inside the function is able to modify the object passed as the argument and such changes remain after the function execution is over.
In the foo
function below, when foo(original)
is executed, the object reference of the argument original
is copied to the parameter items
. Now the function is able to modify the list object (i.e., the list object being pointed to by both original
and items
), and the changes remain in the list object even after the function has finished.
| → | [Click here to visualize the execution]
|
Contrast the above example with the one below. The bar(items)
function assigns a new list to items
parameter. That means items
is no longer pointing to the list object that was passed in as the argument. After the function is executed, the original
list remains the same as before.
| → | [Click here to visualize the execution]
|
If the argument passed to a function is of an immutable type, the function receives the reference to a copy of the argument. That means the code inside the function is unable to modify the object passed as the argument. However, it can return a new object that is based on the given object copy.
In this example, the function increment(age)
is given an immutable object 25
as the argument. Although the parameter v
that received the argument is assigned a new object (i.e., v = v + 1
) inside the function, that change is not reflected in the argument age
. That is because v
is given a reference to a copy of age
, not a reference to the actual object in age
.
| → | [Click here to visualize the execution]
|
Working with Lists
You can use del
to delete an item at a specific position of a list.
The code below shows how to use del
to delete items in a list.
| → |
|
You can use the slice notation list_name[start_index:end_index]
to copy a into a new list. If the start_index
is omitted, it means 'from the beginning of the list'. If the end_index
is omitted, it means 'till the end of the list'.
Some example of slicing to get a sub list:
| → |
|
Note that slicing gives you a copy of a portion of the original list i.e., you get a new list.
In the example below, the first item 0a
of the list letters
is deleted after taking the first two elements as a sub list. Note how the item 0a
still remains in the sub list after it has been deleted from the original list.
letters = ['0a', '1b', '2c', '3d', '4e']
first_two_letters = letters[:2]
print('original list:', letters)
print('sub list :', first_two_letters)
del letters[0] # delete first element in original list
print('original list:', letters)
print('sub list :', first_two_letters)
original list: ['0a', '1b', '2c', '3d', '4e']
sub list : ['0a', '1b']
original list: ['1b', '2c', '3d', '4e']
sub list : ['0a', '1b']
You can use the len
function to find the number of items in a list.
| → |
|
You can use the +
operator to combine multiple lists into a new list.
| → |
|
You can use the *
operator to create a new list by replicating an existing list multiple times.
| → |
|
You can use in
or not in
to check if an item is in a list.
| → |
|
You can traverse through items in a list using the for item_name in list_name:
notation.
| → |
|
The enumerate
function can help you easily maintain an iteration counter while traversing a list.
| → |
|
Methods
In Python, everything is an object. For example, 'Hello'
is a string object, 14
is an integer object, [2.0, 3.5]
is a list object that contains two float objects, and so on.
Accordingly, most Python objects have methods. Methods represent what the object can do. They are functions attached to an object and are executed in relation to the attached object. Methods are executed using object.method_name
notation. The methods an object will have depend on the type of object e.g., methods a string object will have are different from the methods a list object will have.
The first line below uses the strip()
method of the string object to get rid of leading and trailing spaces. The second statement uses the replace()
method to replace spaces with dashes.
| → |
|
Some methods return an object. In such cases another method can be called on the returned object immediately. Such calling of methods on the returned object is called method chaining.
In the example of method chaining given below, 'aa bb cc '.strip()
returns a string object 'aa bb cc'
on which the replace(' ', '_')
method is called. That returns a string object 'aa_bb_cc'
on which the upper()
method is called. That method converts the string into its upper case, giving us the final output of 'AA_BB_CC'
| → |
|
List Methods
Here are some useful list methods:
Method | Description |
---|---|
index() | finds the index of an item in a list |
append() | adds an item to the end of the list |
insert() | inserts an item to a specific position in the list |
remove() | removes the specified item from the list |
sort() | sorts items in the list |
Here are some examples of how those list methods can be used:
pets = ['Cats', 'Dogs', 'Hamsters']
print('Hamster is at', pets.index('Hamsters'))
print('Dog is at', pets.index('Dogs'))
pets.append('Parrots')
print('After appending Parrots', pets)
pets.insert(1, 'Rabbits')
print('After inserting Rabbits', pets)
pets.remove('Cats')
print('After removing Cats', pets)
pets.sort()
print('After sorting', pets)
pets.sort(reverse=True)
print('After reverse-sorting', pets)
Hamster is at 2
Dog is at 1
After appending Parrots ['Cats', 'Dogs', 'Hamsters', 'Parrots']
After inserting Rabbits ['Cats', 'Rabbits', 'Dogs', 'Hamsters', 'Parrots']
After removing Cats ['Rabbits', 'Dogs', 'Hamsters', 'Parrots']
After sorting ['Dogs', 'Hamsters', 'Parrots', 'Rabbits']
After reverse-sorting ['Rabbits', 'Parrots', 'Hamsters', 'Dogs']
Note how in the example above sort(reverse=True)
sorts the list in the reverse order. This notation of method(parameter_name=argument)
is known as passing a keyword argument. Explanation: Some methods/functions has optional parameters. To specify an argument for such an optional parameter, we have to specify which parameter we are supplying. In the case of sort(reverse=True)
, we are supplying the boolean argument True
to the optional parameter named reverse
.
The print
function takes an optional parameter end
. If you provide it with an empty string i.e., end=''
, the function will not print a line break at the end. The example below illustrates the difference it makes.
| → |
|
Note that some methods the object while other methods don't.
In the example below, lower()
(a non-mutating method) returns a new string that is in lower case while the original string remains unchanged. However, sort()
(a mutating method) changes the list object it is attached to (and does not return anything).
| → |
|
| → |
|