Chapter 9 Lists
9.2 List Mutation¶
There are two ways to make changes to a list object. The first is setitem and setslice, which directly replace an item or a slice in a list object, respectively, and delitem and delslice, which delete an item or a slice from a list object, respectively.
The syntax of the setitem operation takes the form of the assignment statement - the = symbol is used for assigning the value on its right hand side to a ‘variable’ on its left hand side. The only difference is that for the setslice operation, the ‘variable’ on the left hand side is an item from a list instead of an identifier.
As shown by the examples above, the syntax for specifying a list item in the setitem operation is the same as that in the getitem operation: an integer in a pair of square brackets is used to indicate the index of the item. The meaning of the = symbol for the setitem operation is consistent with the assignment statement (i.e. assigning a value to an identifier). In the setitem operation, the list item plays the role of an identifier, the value of which changes according to the assigment.
In memory, a list object can be treated as a sequence of binding slots, as shown on the right hand side of Fig. 9.1. The functionality of = in both variable assignment and setitem is to change bindings. The only difference is that, for the former, the change is in a binding table, but for the latter, the change is in a list object.
Suppose that two identifiers are bound to the same list object. Once the list object is modified, the value of both identifiers changes.
Fig. 9.1 illustrates the changes in memory when the setitem operation is performed. Before y[0] = ‘A’ is executed, the identifiers x and y in the global binding table point to the same list object. y[0] = ‘A’ modifies the first item of the list object without affecting the global binding table. As a result, the value of both x and y is modified.
![Fig. 9.1 Memory structure for shared list modification.](../_images/fig9-1.jpeg)
Fig. 9.1 Memory structure for shared list modification.¶
The setslice operation has the same syntax as the setitem operation, except that the left hand side of the = symbol specifies a slice from a list object rather than an item, and the right hand side of the = symbol must be convertable to a list object.
>>> l = [1, 2, 3]
>>> l[0:2] = [-1, -2] # replaces a slice
>>> l
[-1, -2, 3]
>>> l[1:3] = [2, 3, 4] # replaces a slice with a longer slice
>>> l
[-1, 2, 3, 4]
>>> l[-2:] = [-3] # replaces a slice with a shorter slice
>>> l
[-1, 2, -3]
>>> l[1:2] = [] #deletes a slice
>>> l
[-1, -3]
>>> l[1:1] = [-2] # inserts a slice
>>> l
[-1, -2, -3]
In the example above, the slices specified by the left hand side of the = symbols are replaced with the lists specified by the right hand side. Two cases are worth noting. The first is l[1 : 2] = [ ], where the right hand side is an empty list - the effect of replacing a non-empty slice with an empty list is to delete the corresponding slice. The second is l[1 : 1] = [−2], where the left hand side is an empty slice - the effect of replacing an empty slice with a non-empty list is to insert a list into the location specified by the empty slice. As discussed before, the starting index of a slice indicates the location before the corresponding item.
The setslice operation does not create any binding relationship between the list on the left hand side of = and the list on the right hand side. As a result, two independent lists remain independent after a setslice operation between them:
>>> l = [1, 2, 3]
>>> s = ['a', 'b']
>>> l[2:] = s
>>> l
[1, 2, 'a', 'b']
>>> l[3] = 'c'
>>> l
[1, 2, 'a', 'c']
>>> s
['a', 'b']
In the example above, the slice l[2 :] was set to the value of s. After this operation, l is further changed. The change l[3] = ‘c’ modifies the new slice on l, but does not change the value of s. The reason behind can be explained by Fig. 9.2, which shows the mechanism of setslice. The new slice in e is a copy of s, rather than s itself. In fact, the right hand side for setslice can also be immutable sequences, such as tuples and strings.
>>> l = [1, 2, 3]
>>> t = (1.0, 2.0, 3.0)
>>> s = 'abc'
>>> l[2:] = t
>>> l
[1, 2, 1.0, 2.0, 3.0]
>>> l[4:] = s
>>> l
[1, 2, 1.0, 2.0, 'a', 'b', 'c']
![Fig. 9.2 Memory structure for the setslice operation.](../_images/fig9-2.jpeg)
Fig. 9.2 Memory structure for the setslice operation.¶
The reverse operation of the assignment statement is the del statement. del can also be applied to list items and slices, indicating the delitem and delsice operations, respectively.
>>> l = [1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> del l[0] # removes the first item
>>> l
[2, 3, 4, 5, 6, 7, 8, 9]
>>> del l[2:5] # removes a slice
>>> l
[2, 3, 7, 8, 9]
>>> del l[-1]
>>> l
[2, 3, 7, 8]
One final note about the setitem, setslice, delitem and delslice operations is that they are applicable only to lists, which are mutable, but not to strings or tuples, which are immutable.
>>> s='abc'
>>> t=(1,2,3)
>>> s[0]='d' # no string setitem
Traceback (most recent call last):
File "<stdin >", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> t[1:3]=(0,) # no tuple setslice
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> del s[1:] # no string delslice
Traceback (most recent call last):
File "<stdin >", line 1, in <module >
TypeError: 'str' object does not support item deletion
>>> del t[-1] # no tuple delitem
Traceback (most recent call last):
File "<stdin >", line 1, in <module >
TypeError: 'tuple ' object doesn 't support item deletion
Methods The second way of modifying a list object is to call its methods. A method is a special function, which is bound to a specific object, performing operations on the object. The syntax of a method call is
<object >.<method name >(<arguments >)
A method call starts with an identifier, which represents the object, followed by a dot (.), and then the name of the method that is bound to the object, and finally a comma-separated list of arguments, enclosed in a pair of parentheses. The syntax of a method call can be split into two parts, the first being the object and the dot, and the second being the method name and the arguments. The second part is identical to a function call, while the first half indicates that the method belongs to a specific object.
List modification methods. There are two commonly-used methods that add an item to a list: append, which takes a single argument and adds it to the back of a list, and insert, which takes two arguments specifying an index and a new item, respectively, inserting the item into the list, so that the index of the new item after the insertion is the specified index. Both methods are procedures, with a None return value.
>>> l = [1, 2, 3]
>>> l.append(4)
>>> l
[1, 2, 3, 4]
>>> l.insert(0, 0)
>>> l
[0, 1, 2, 3, 4]
>>> l1 = l
>>> l.insert(3, 'a')
>>> l
[0, 1, 2, 'a', 3, 4]
>>> l1
[0, 1, 2, 'a', 3, 4]
>>> l.insert(-2, 'b')
>>> l
[0, 1, 2, 'a', 'b', 3, 4]
>>> l1
[0, 1, 2, 'a', 'b', 3, 4]
In the example above, the identifies l and l1 are bound to the same object. The values of l1 changes after the insert and append methods are applied to l, showing that the methods modify the list object itself. The effect of append(x) is the same as that of the setslice operation l[len(l):len(l)]=x, while the effect of l.insert(i, x) is the same as the setslice operation l[i:i]=[x].
A method that extends a list by concatenating it with another list is extend, which takes a list argument, and concatenates it with the list object.
>>> l = [1, 2, 3]
>>> x = [4, 5, 6]
>>> l.extend(x)
>>> l
[1, 2, 3, 4, 5, 6]
>>> l[-1] = 0
>>> l
[1, 2, 3, 4, 5, 0]
>>> x
[4, 5, 6]
As the example above demonstrates, the extend method makes a copy of the argument x, and updates the object l with the copy of x. A modification to the new slice in l does not affect the value of x. The effect of l.extend(x) is the same as that of the setslice operation l[len(l):len(l)] = x.
A method that deletes an item from a list is remove, which takes one argument, and deletes the first occurrence of the argument from the list. If the input argument is not in the list, a value error is reported.
>>> l = [1, 2, 3, 4, 5, 1]
>>> l.remove(1)
>>> l
[2, 3, 4, 5, 1]
>>> l.remove(5)
>>> l
[2, 3, 4, 1]
>>> l.remove(6)
Traceback (most recent call last):
File "<stdin >", line 1, in <module >
ValueError: list.remove(x): x not in list
Another method that modifies a list is reverse, which reverses the order of the list. The reverse method takes no arguments, and returns None.
>>> l = [1, 2, 3]
>>> l.reverse()
>>> l
[3, 2, 1]
Another method that changes the order of a list is sort, which puts items in the list in ascending order. Similar to reverse, the sort method takes no arguments and returns None.
>>> l = [1, 5, 3, 4, 2, 7, 6, 8, 10, 9]
>>> l.sort()
>>> l
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Check your understanding
- [4, 2, True, 8, 6, 5]
- Item assignment does not insert the new item into the list.
- [4, 2, True, 6, 5]
- Yes, the value True is placed in the list at index 2. It replaces 8.
- Error, it is illegal to assign
- Item assignment is allowed with lists. Lists are mutable.
9.2Q-1: What is printed by the following statements?
alist = [4, 2, 8, 6, 5]
alist[2] = True
print(alist)
- [ ]
- The empty list is at index 4.
- 3.14
- Yes, 3.14 is at index 5 since we start counting at 0 and sublists count as one item.
- False
- False is at index 6.
9.2Q-2: What is printed by the following statements?
alist = [3, 67, "cat", [56, 57, "dog"], [ ], 3.14, False]
print(alist[5])
- Error, you cannot use the upper method on a list.
- alist[2] is the string cat so the upper method is legal
- 2
- 2 is the index. We want the item at that index.
- CAT
- Yes, the string cat is upper cased to become CAT.
9.2Q-3: What is printed by the following statements?
alist = [3, 67, "cat", [56, 57, "dog"], [ ], 3.14, False]
print(alist[2].upper())
- 56
- Indexes start with 0, not 1.
- c
- Yes, the first character of the string at index 2 is c
- cat
- cat is the item at index 2 but then we index into it further.
- Error, you cannot have two index values unless you are using slicing.
- Using more than one index is fine. You read it from left to right.
9.2Q-4: What is printed by the following statements?
alist = [3, 67, "cat", [56, 57, "dog"], [ ], 3.14, False]
print(alist[2][0])
- [4, 2, 8, 6, 5, False, True]
- True was added first, then False was added last.
- [4, 2, 8, 6, 5, True, False]
- Yes, each item is added to the end of the list.
- [True, False, 4, 2, 8, 6, 5]
- append adds at the end, not the beginning.
9.2Q-5: What is printed by the following statements?
alist = [4, 2, 8, 6, 5]
alist.append(True)
alist.append(False)
print(alist)
- [False, 4, 2, True, 8, 6, 5]
- Yes, first True was added at index 2, then False was added at index 0.
- [4, False, True, 2, 8, 6, 5]
- insert will place items at the index position specified and move everything down to the right.
- [False, 2, True, 6, 5]
- insert does not remove anything or replace anything.
9.2Q-6: What is printed by the following statements?
alist = [4, 2, 8, 6, 5]
alist.insert(2, True)
alist.insert(0, False)
print(alist)
© Copyright 2024 GS Ng.