|
| 1 | +Python allows you to manipulate a [`list`][list] in a lot of ways. A `list` is simple a collection of objects of [type `List`][list std type]. They are mutable, ordered and indexed. Let's look at the methods that are available to manipulate a `list` object. |
| 2 | + |
| 3 | +When you manipulate a list with a list-method, you are changing the properties of the list you pass. That is, **you will alter the list** object that is being used with the list-method. If you do not want to change the original list, you need to copy the list and then work on the copied list. |
| 4 | + |
| 5 | +To begin, you first need a `list` object to apply the methods to. |
| 6 | + |
| 7 | +```python |
| 8 | +>>> empty_list = list() # (or) empty_list = [] |
| 9 | +>>> empty_list |
| 10 | +[] |
| 11 | +``` |
| 12 | + |
| 13 | +# Add Items to a list |
| 14 | + |
| 15 | +Methods to add items: |
| 16 | + |
| 17 | +1. `list.append()` |
| 18 | +2. `list.insert()` |
| 19 | +3. `list.extend()` |
| 20 | + |
| 21 | +If you want to add an item after all the items of an existing list, you use the list-method `append()` for it. As the name indicates, `append()` attaches the item at the **end** of the list. |
| 22 | + |
| 23 | +```python |
| 24 | +>>> numbers = [1, 2, 3] |
| 25 | +>>> numbers.append(9) |
| 26 | +>>> numbers |
| 27 | +[1, 2, 3, 9] |
| 28 | +``` |
| 29 | + |
| 30 | +Rather than appending, the `insert()` method gives you the ability to add the item to a _specific index_ in the list. |
| 31 | + |
| 32 | +`.insert()` takes 2 parameters: |
| 33 | + |
| 34 | +1. the index of the item before which you want the new item to appear |
| 35 | +2. the item to be inserted |
| 36 | + |
| 37 | +Note: If the given index is 0, the item will be added to the start of the list. If the supplied index is greater than the last index of the list, the item will be added in the last position, this is the equivalent of using the `append()` method. |
| 38 | + |
| 39 | +```python |
| 40 | +>>> numbers = [1, 2, 3] |
| 41 | +>>> numbers.insert(0, -2) |
| 42 | +>>> numbers |
| 43 | +[-2, 1, 2, 3] |
| 44 | +>>> numbers.insert(1, 0) |
| 45 | +>>> numbers |
| 46 | +[-2, 0, 1, 2, 3] |
| 47 | +``` |
| 48 | + |
| 49 | +If you have an iterable that you would like to _combine_ with your current list (concatenating the two), you can use the `list.extend()` method. `extend()` will unpack the supplied iterable and add its elements, in the same order, to your list (_using `.append()` in this circumstance would add the entire iterable as a **single item**._). |
| 50 | + |
| 51 | +The extend method takes an iterable as its parameter: |
| 52 | + |
| 53 | +```python |
| 54 | +>>> numbers = [1, 2, 3] |
| 55 | +>>> other_numbers = [5, 6, 7] |
| 56 | + |
| 57 | +>>> numbers.extend(other_numbers) |
| 58 | +>>> numbers |
| 59 | +[1, 2, 3, 5, 6, 7] |
| 60 | + |
| 61 | +>>> numbers.extend([8, 9]) |
| 62 | +>>> numbers |
| 63 | +[1, 2, 3, 5, 6, 7, 8, 9] |
| 64 | +``` |
| 65 | + |
| 66 | +# Remove Items from a list |
| 67 | + |
| 68 | +Methods to remove items: |
| 69 | + |
| 70 | +1. `list.remove()` |
| 71 | +2. `list.pop()` |
| 72 | +3. `list.clear()` |
| 73 | + |
| 74 | +If you want to delete an item from a list, you can use the `list.remove()` method, passing the item to be removed as an argument. `remove()` will throw a `ValueError` if the item is not present in the list. |
| 75 | + |
| 76 | +```python |
| 77 | +>>> numbers = [1, 2, 3] |
| 78 | +>>> numbers.remove(2) |
| 79 | +>>> numbers |
| 80 | +[1, 3] |
| 81 | +>>> numbers.remove(0) |
| 82 | +ValueError: list.remove(x): x not in list |
| 83 | +``` |
| 84 | + |
| 85 | +Alternatively, using the `list.pop()` method will both remove **and** `return` an element for use. `pop()` takes one optional parameter: the index of the item you need to remove and receive. If you specify an index number higher than the length of the list, you will get an `IndexError`. If the optional index argument is not specified, the last element of the list will be removed and returned to you. |
| 86 | + |
| 87 | +```python |
| 88 | +>>> numbers = [1, 2, 3] |
| 89 | +>>> numbers.pop(0) |
| 90 | +1 |
| 91 | +>>> numbers |
| 92 | +[2, 3] |
| 93 | +>>> numbers.pop() |
| 94 | +3 |
| 95 | +>>> numbers |
| 96 | +[2] |
| 97 | +``` |
| 98 | + |
| 99 | +If you want to remove all the items from a `list` you can use the `list.clear()` method. It does not take any parameters. |
| 100 | + |
| 101 | +```python |
| 102 | +>>> numbers = [1, 2, 3] |
| 103 | +>>> numbers.clear() |
| 104 | +>>> numbers |
| 105 | +[] |
| 106 | +``` |
| 107 | + |
| 108 | +# Reverse and reorder a list |
| 109 | + |
| 110 | +You can reverse the order of a list with the `list.reverse()` method. |
| 111 | + |
| 112 | +```python |
| 113 | +>>> numbers = [1, 2, 3, 0] |
| 114 | +>>> numbers.reverse() |
| 115 | +>>> numbers |
| 116 | +[0, 3, 2, 1] |
| 117 | +``` |
| 118 | + |
| 119 | +A list can be re-ordered _**in place**_ with the help of the `list.sort()` method. Internally, Python uses [`Timsort`][timsort] to arrange the list. The default order is _ascending_. The Python docs offer some [additional tips and techniques for sorting][sorting how to] lists effectively. |
| 120 | + |
| 121 | +If you have a list of names and you want to sort them: |
| 122 | + |
| 123 | +```python |
| 124 | +>>> names = ["Tony", "Natasha", "Thor", "Bruce"] |
| 125 | +>>> names.sort() |
| 126 | +>>> names |
| 127 | +["Bruce", "Natasha", "Thor", "Tony"] |
| 128 | +``` |
| 129 | + |
| 130 | +If you want the sort to be in descending order, you can pass the reverse argument: |
| 131 | + |
| 132 | +```python |
| 133 | +>>> names = ["Tony", "Natasha", "Thor", "Bruce"] |
| 134 | +>>> names.sort(reverse=True) |
| 135 | +>>> names |
| 136 | +["Tony", "Thor", "Natasha", "Bruce"] |
| 137 | +``` |
| 138 | + |
| 139 | +For cases where changing your original list is undesirable, the built-in [`sorted()`][sorted] can be used to return a new, sorted copy of your original list. |
| 140 | + |
| 141 | +# Occurrences of an item in a list |
| 142 | + |
| 143 | +You can find the occurrences of an element in a list with the help of `list.count()`. It takes the item you need to tally as its argument and returns the total number of times it appears on the list. |
| 144 | + |
| 145 | +```python |
| 146 | +>>> items = [1, 4, 7, 8, 2, 9, 2, 1, 1, 0, 4, 3] |
| 147 | +>>> items.count(1) |
| 148 | +3 |
| 149 | +``` |
| 150 | + |
| 151 | +# Find the index of an item |
| 152 | + |
| 153 | +The `list.index()` method will give you the index number of the _first occurrence_ of an item you pass in. If there are no occurrences of the item, a `ValueError` is raised. If you do not need the exact position of an item and are only checking that it is present inside the list, the built-in `in` operator is more efficient. |
| 154 | + |
| 155 | +Indexing is zero-based, meaning the position of the first item is `0`. |
| 156 | + |
| 157 | +```python |
| 158 | +>>> items = [7, 4, 1, 0, 2, 5] |
| 159 | +>>> items.index(4) |
| 160 | +1 |
| 161 | +>>> items.index(10) |
| 162 | +ValueError: 10 is not in list |
| 163 | +``` |
| 164 | + |
| 165 | +You can provide start and end indices to search within a specific section of the list. |
| 166 | + |
| 167 | +```python |
| 168 | +>>> names = ["Tina", "Leo", "Thomas", "Tina", "Emily", "Justin"] |
| 169 | +>>> names.index("Tina") |
| 170 | +0 |
| 171 | +>>> names.index("Tina", 2, 5) |
| 172 | +3 |
| 173 | +``` |
| 174 | + |
| 175 | +# Make a copy of a list |
| 176 | + |
| 177 | +Remember that variables in Python are labels that point to underlying objects. |
| 178 | + |
| 179 | +Lists are _collections_ of object references ordered by an index. If you assign a list object to a new variable name, any change you make to the list using the new variable name will also _impact_ the original variable. Both variable names point to the **same list object** which is modified. |
| 180 | + |
| 181 | +```python |
| 182 | +>>> actual_names = ["Tony", "Natasha", "Thor", "Bruce"] |
| 183 | + |
| 184 | +# Both variable names point to the same list |
| 185 | +>>> same_list = actual_names |
| 186 | + |
| 187 | +>>> same_list.append("Clarke") |
| 188 | +>>> same_list |
| 189 | +["Tony", "Natasha", "Thor", "Bruce", "Clarke"] |
| 190 | +>>> actual_names |
| 191 | +["Tony", "Natasha", "Thor", "Bruce", "Clarke"] |
| 192 | +``` |
| 193 | + |
| 194 | +If you need to use a new list, use the `list.copy()` method or a _slice_ to make a `shallow_copy` of the list. Using `copy()` or slicing will create a new list _object_, but will not create new objects for the list items. (More about the differences between a `shallow_copy` and a `deep_copy` later). |
| 195 | + |
| 196 | +```python |
| 197 | +>>> names = ["Tony", "Natasha", "Thor", "Bruce"] |
| 198 | + |
| 199 | +# The two list objects are independents, but they contain of the same items. |
| 200 | +>>> new_list = names.copy() |
| 201 | + |
| 202 | +>>> new_list.append("Clarke") |
| 203 | +>>> new_list |
| 204 | +["Tony", "Natasha", "Thor", "Bruce", "Clarke"] |
| 205 | +>>> names |
| 206 | +["Tony", "Natasha", "Thor", "Bruce"] |
| 207 | +``` |
| 208 | + |
| 209 | +_Slicing_ will also make a second set of references that can then be changed without the danger of unintentional mutation of elements: |
| 210 | + |
| 211 | +```python |
| 212 | +>>> names = ["Tony", "Natasha", "Thor", "Bruce"] |
| 213 | + |
| 214 | +# This is equivalent to using names.copy() |
| 215 | +>>> new_list = names[:] |
| 216 | + |
| 217 | +>>> new_list.append("Clarke") |
| 218 | +>>> new_list |
| 219 | +["Tony", "Natasha", "Thor", "Bruce", "Clarke"] |
| 220 | +>>> names |
| 221 | +["Tony", "Natasha", "Thor", "Bruce"] |
| 222 | +``` |
| 223 | + |
| 224 | +This reference constraint becomes exacerbated when working with nested/multiplied lists: |
| 225 | + |
| 226 | +```python |
| 227 | +from pprint import pprint |
| 228 | + |
| 229 | +# This will produce a game grid that is 8x8, pre-populated with zeros |
| 230 | +>>> game_grid = [[0]*8] *8 |
| 231 | + |
| 232 | +>>> pprint(game_grid) |
| 233 | +[[0, 0, 0, 0, 0, 0, 0, 0], |
| 234 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 235 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 236 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 237 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 238 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 239 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 240 | + [0, 0, 0, 0, 0, 0, 0, 0]] |
| 241 | + |
| 242 | +# An attempt to put a "X" in the bottom right corner |
| 243 | +>>> game_grid[7][7] = "X" |
| 244 | + |
| 245 | +# This doesn't work because all the rows are referencing the same underlying list object. |
| 246 | +>>> pprint(game_grid) |
| 247 | +[[0, 0, 0, 0, 0, 0, 0, 'X'], |
| 248 | + [0, 0, 0, 0, 0, 0, 0, 'X'], |
| 249 | + [0, 0, 0, 0, 0, 0, 0, 'X'], |
| 250 | + [0, 0, 0, 0, 0, 0, 0, 'X'], |
| 251 | + [0, 0, 0, 0, 0, 0, 0, 'X'], |
| 252 | + [0, 0, 0, 0, 0, 0, 0, 'X'], |
| 253 | + [0, 0, 0, 0, 0, 0, 0, 'X'], |
| 254 | + [0, 0, 0, 0, 0, 0, 0, 'X']] |
| 255 | +``` |
| 256 | + |
| 257 | +As mentioned earlier, if your list contains _variables_ or nested data structures, those second-level references will **not be copied** via `copy()` or slice. To copy an entire tree of containers, references, and objects, you need to use `list.deep_copy()` or a `list comprehension`. For a detailed explanation of list behaviors, see this excellent [making a game board][making a game board] article. |
| 258 | + |
| 259 | +```python |
| 260 | +from pprint import pprint |
| 261 | + |
| 262 | +# This loop will safely produce a game grid that is 8x8, pre-populated with zeros |
| 263 | +>>> game_grid = [] |
| 264 | +>>> filled_row = [0] * 8 |
| 265 | +>>> for row in range(8): |
| 266 | +... game_grid.append(filled_row.copy()) |
| 267 | + |
| 268 | +>>> pprint(game_grid) |
| 269 | +[[0, 0, 0, 0, 0, 0, 0, 0], |
| 270 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 271 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 272 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 273 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 274 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 275 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 276 | + [0, 0, 0, 0, 0, 0, 0, 0]] |
| 277 | + |
| 278 | +# An attempt to put a "X" in the bottom right corner |
| 279 | +>>> game_grid[7][7] = "X" |
| 280 | + |
| 281 | +# The game grid now works the way we expect it to |
| 282 | +>>> pprint(game_grid) |
| 283 | +[[0, 0, 0, 0, 0, 0, 0, 0], |
| 284 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 285 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 286 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 287 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 288 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 289 | + [0, 0, 0, 0, 0, 0, 0, 0], |
| 290 | + [0, 0, 0, 0, 0, 0, 0, 'X']] |
| 291 | +``` |
| 292 | + |
| 293 | +[list]: https://docs.python.org/3/tutorial/datastructures.html#more-on-lists |
| 294 | +[list std type]: https://docs.python.org/3.9/library/stdtypes.html#list |
| 295 | +[timsort]: https://en.wikipedia.org/wiki/Timsort |
| 296 | +[sorted]: https://docs.python.org/3/library/functions.html#sorted |
| 297 | +[making a game board]: https://nedbatchelder.com/blog/201308/names_and_values_making_a_game_board.html |
| 298 | +[sorting how to]: https://docs.python.org/3/howto/sorting.html |
0 commit comments