List Methods and Mutability

Lists are mutable, so we can change a list, such as by adding, removing, or modifying an item:

In [1]:
gross_earnings = [361681, 740741, 396105,  284600, 249154]

# To modify an item, we can use indexing and an assignment statment.
gross_earnings[1] = gross_earnings[1] * 1.05
print(gross_earnings[1])
777778.05

list methods

Many of list methods modify the list:

In [2]:
print(gross_earnings)
[361681, 777778.05, 396105, 284600, 249154]
In [3]:
gross_earnings.append(740741)  # Adds the argument to the end of the list.
print(gross_earnings)
[361681, 777778.05, 396105, 284600, 249154, 740741]
In [4]:
gross_earnings.extend([200000, 300000, 400000]) # Append the items in the list argument to the list.

print(gross_earnings)
[361681, 777778.05, 396105, 284600, 249154, 740741, 200000, 300000, 400000]
In [5]:
print(gross_earnings.pop()) # Removes the last item in the list and returns that item.
print(gross_earnings)
400000
[361681, 777778.05, 396105, 284600, 249154, 740741, 200000, 300000]
In [6]:
print(gross_earnings.pop(1)) # Removes the item the given index and returns that item.
print(gross_earnings)
777778.05
[361681, 396105, 284600, 249154, 740741, 200000, 300000]
In [7]:
gross_earnings.remove(200000)  # Removes the first occurence of the argument; error if argument not in list.
print(gross_earnings)
[361681, 396105, 284600, 249154, 740741, 300000]
In [8]:
gross_earnings.reverse()  # Reverses the list.
print(gross_earnings)
[300000, 740741, 249154, 284600, 396105, 361681]
In [9]:
gross_earnings.sort() # Sorts the list.
print(gross_earnings)
[249154, 284600, 300000, 361681, 396105, 740741]
In [10]:
gross_earnings.insert(1, 250000) # Inserts the object at the given index, moving items to make room.
print(gross_earnings)
[249154, 250000, 284600, 300000, 361681, 396105, 740741]

There are also list methods that given information about lists, but don't modify them:

In [11]:
L = ['a', 'b', 'c', 'b', 'b', 'a']
L.count('b')  # Counts the number of occurences of 'b' in list L.
Out[11]:
3
In [12]:
L.index('b') # Returns the index of the first occurence of 'b' in list L; error if not present.
Out[12]:
1
In [13]:
L.index('d')  # An error occurs if the item is not in the list.
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-13-e47cdc506f2e> in <module>()
----> 1 L.index('d')

ValueError: 'd' is not in list

Writing code that modifies a list and code that does not

For this next problem, we have a list of measurements and we want to filter them to get only those that are below a particular threshold. We will do this two different ways:

  1. By creating a new list with the measurements below the threshold and leaving the original list unchanged.
  2. By removing those measurements at or above the threshold from the original list, leaving only those that are below the threshold in the list. Notice that with this approach, we are discarding some elements!

Here is the first solution:

In [22]:
def get_scaled_measurements(measurements, factor):
    """ (list of number, int) -> list of number
    
    Return a new list consisting of the values from measurements scaled by
    factor, in the same order as in measurements.
    
    >>> get_scaled_measurements([1, 2, 3, 4], 3)
    [3, 6, 9, 12]
    >>> get_scaled_measurements([1, 2, 108, 3, 4], 50)
    [50, 100, 5400, 150, 200]
    >>> get_scaled_measurements([], 7)
    []
    """
    
    scaled_measurements = []
    
    for i in range(len(measurements)):
        scaled_measurements.append(measurements[i] * factor)
            
    return scaled_measurements

print(get_scaled_measurements([1, 2, 3, 4], 3))
print(get_scaled_measurements([1, 2, 108, 3, 4], 50))
print(get_scaled_measurements([], 7))
[3, 6, 9, 12]
[50, 100, 5400, 150, 200]
[]

Notice that the list passed as an argument to this function is not changed by the function call:

In [35]:
L = [1, 2, 108, 3, 4]
print(get_scaled_measurements(L, 50))
print(L)
[50, 100, 5400, 150, 200]
[1, 2, 108, 3, 4]

Now, let's try an alternative approach where we modify the list:

In [29]:
def scale_measurements(measurements, factor):
    """ (list of number, int) -> NoneType
    
    Modify measurements so that it contains the original values scaled by
    factor, in the same order as they originally appeared.
    
    >>> L = [1, 2, 3, 4]
    >>> scale_measurements(L, 3)
    >>> L
    >>> L = [1, 2, 108, 3, 4]
    >>> scale_measurements(L, 50)
    >>> L
    >>> L = []
    >>> scale_measurements(L, 7)
    >>> L
    """
    
    for i in range(len(measurements)):
        measurements[i] = measurements[i] * factor
            
            
L = [1, 2, 3, 4]
scale_measurements(L, 3)
print(L)
L = [1, 2, 108, 3, 4]
scale_measurements(L, 50)
print(L)
L = []
scale_measurements(L, 7)
print(L)
[3, 6, 9, 12]
[50, 100, 5400, 150, 200]
[]

Unlike the first approach, the lists are modified by the function. This function does not contain a return statement, so by default, it returns the value None.

Practice Exercise: Building a new list

Complete the following function according to its docstring description:

In [36]:
def report_digits(original_list):
    """ (list of str) -> list of bool 
    
    >>> report_digits(['98', 'a', '5'])
    [True, False, True]
    
    Return a new list in which each item is True if the correponding item from 
    original_list is composed entirely of digits, and False otherwise.
    """
    

Practice Exercise: Modifying an existing list

Complete the following function according to its docstring description:

In [38]:
def replace_digits(original_list):
    """ (list of str) -> NoneType
    
    >>> data = ['98', 'a', '5']
    >>> replace_digits(data)
    >>> data
    
    Replace each item in original_list with True if it is composed entiredly of digits,
    and with False otherwise.
    """