DekGenius.com
[ Team LiB ] Previous Section Next Section

6.2 Lists in Action

Perhaps the best way to understand lists is to see them at work. Let's once again turn to some simple interpreter interactions to illustrate the operations in Table 6-1.

6.2.1 Basic List Operations

Lists respond to the + and * operators much like strings; they mean concatenation and repetition here too, except that the result is a new list, not a string. In fact, lists respond to all of the general sequence operations used for strings.

% python
>>> len([1, 2, 3])                    # Length
3
>>> [1, 2, 3] + [4, 5, 6]             # Concatenation
[1, 2, 3, 4, 5, 6]
>>> ['Ni!'] * 4                       # Repetition
['Ni!', 'Ni!', 'Ni!', 'Ni!']
>>> 3 in [1, 2, 3]                    # Membership (1 means true)
1
>>> for x in [1, 2, 3]: print x,      # Iteration
...
1 2 3

We talk more about for iteration and the range built-ins in Chapter 10, because they are related to statement syntax; in short, for loops step through items in a sequence. The last entry in Table 6-1, list comprehensions, are covered in Chapter 14; they are a way to build lists by applying expressions to sequences, in a single step.

Although + works the same for lists and strings, it's important to know that it expects the same sort of sequence on both sides—otherwise you get a type error when the code runs. For instance, you cannot concatenate a list and a string, unless you first convert the list to a string using backquotes, str, or % formatting. You could also convert the string to a list; the list built-in function does the trick:

>>> `[1, 2]` + "34"         # Same as "[1, 2]" + "34"
'[1, 2]34'
>>> [1, 2] + list("34")     # Same as [1, 2] + ["3", "4"]
[1, 2, '3', '4']

6.2.2 In dexing, Slicing, and Matrixes

Because lists are sequences, indexing and slicing work the same way, but the result of indexing a list is whatever type of object lives at the offset you specify, and slicing a list always returns a new list:

>>> L = ['spam', 'Spam', 'SPAM!']
>>> L[2]                               # Offsets start at zero.
'SPAM!'
>>> L[-2]                              # Negative: count from the right.
'Spam'
>>> L[1:]                              # Slicing fetches sections.
['Spam', 'SPAM!']

One note here: because you can nest lists (and other types) with lists, you will sometimes need to string together index operations to go deeper into a data structure. For example, one of the simplest ways to represent matrixes (multidimensional arrays) in Python, is as lists with nested sublists. Here's a basic, list-based, 3-by-3, two-dimensional array:

>>> matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

With one index, you get an entire row (really, a nested sublist); with two, you get a item within the row:

>>> matrix[1]
[4, 5, 6]
>>> matrix[1][1]
5
>>> matrix[2][0]
7 
>>> matrix = [[1, 2, 3],
...           [4, 5, 6],
...           [7, 8, 9]]
>>> matrix[1][1]
5

Notice the last portion of this example; lists can naturally span multiple lines if you want them to. Later in this chapter, you'll also see a dictionary-based matrix representation. The NumPy extension mentioned earlier provides other ways to handle matrixes.

6.2.3 Changing Lists in-Place

Because lists are mutable, they support operations that change a list object in-place; that is, the operations in this section all modify the list object directly, without forcing you to make a new copy as you had to for strings. But since Python only deals in object references, the distinction between in-place changes and new objects does matter; if you change an object in place, you might impact more than one reference to it at the same time.

6.2.3.1 Index and slice assignment

When using a list, you can change its contents by assigning to a particular item (offset), or an entire section (slice):

>>> L = ['spam', 'Spam', 'SPAM!']
>>> L[1] = 'eggs'                  # Index assignment
>>> L
['spam', 'eggs', 'SPAM!']
>>> L[0:2] = ['eat', 'more']       # Slice assignment: delete+insert
>>> L                              # Replaces items 0,1
['eat', 'more', 'SPAM!']

Both index and slice assignments are in-place changes—they modify the subject list directly, rather than generating a new list object for the result. Index assignment works much as it does in C, and most other languages: Python replaces the object reference at the designated offset with a new one.

Slice assignment, the last operation in the preceding example, replaces an entire section of a list in a single step. Because it can be a bit complex, it is perhaps best thought of as the combination of two steps:

  1. Deletion. The slice you specify on the left of the = is deleted.

  2. Insertion. The new items on the right are inserted into the list on the left, at the place where the old slice was deleted.

This isn't what really happens,[2] but it tends to help clarify why the number of items inserted doesn't have to match the number of items deleted. For instance, given a list L that has the value [1,2,3], the assignment L[1:2]=[4,5] sets L to the list [1,4,5,3]. Python first deletes the 2 (a one-item slice), then inserts items 4 and 5 where the deleted 2 used to be. It also explains why L[1:2]=[ ] is really a deletion operation.

[2] This description needs elaboration when the value and slice being assigned overlap: L[2:5]=L[3:6], for instance, works fine, because the value to be inserted is fetched before the deletion happens on the left.

6.2.3.2 List method calls

Like strings, Python list objects also support type-specific method calls:

>>> L.append('please')                # Append method call.
>>> L
['eat', 'more', 'SPAM!', 'please']
>>> L.sort(  )                          # Sort list items ('S' < 'e').
>>> L
['SPAM!', 'eat', 'more', 'please']

Methods were introduced in Chapter 5. In brief, they are functions (or attributes that reference functions) that are associated with a particular object. Methods provide type-specific tools; the list methods presented here, for instance, are only available for lists.

The list append method simply tacks a single item (object reference) onto the end of the list. Unlike concatenation, append expects you to pass in a single object, not a list. The effect of L.append(X) is similar to L+[X], but the former changes L in place, and the latter makes a new list.[3] The sort method orders a list in-place; by default, it uses Python standard comparison tests (here, string comparisons), and sorts in ascending fashion. You can also pass in a comparison function of your own to sort.

[3] Unlike + concatenation, append doesn't have to generate new objects, and so is usually faster. You can also mimic append with clever slice assignments: L[len(L):]=[X] is like L.append(X), and L[:0]=[X] is like appending at the front of a list. Both delete an empty slice and insert X, changing L in place quickly like append.

(Beware that append and sort change the associated list object in-place, but don't return the list as a result (technically, they both return a value called None). If you say something like L=L.append(X), you won't get the modified value of L (in fact, you'll lose the reference to the list altogether); when you use attributes such as append and sort, objects are changed as a side effect, so there's no reason to reassign.)

As for strings, other list methods perform other specialized operations. For instance, reverse reverses the list in-place, and the extend and pop methods insert multiple items at the end, and delete an item from the end, respectively:

>>> L = [1, 2]
>>> L.extend([3,4,5])      # Append multiple items.
>>> L
[1, 2, 3, 4, 5]
>>> L.pop(  )                # Delete, return last item.
5
>>> L
[1, 2, 3, 4]
>>> L.reverse(  )            # In-place reversal.
>>> L
[4, 3, 2, 1]

In some types of programs, the list pop method used here is often used in conjuction with append to implement a quick last-in-first-out stack structure. The end of the list serves as the top of the stack:

>>> L = [  ]
>>> L.append(1)                    # Push onto stack.
>>> L.append(2)
>>> L
[1, 2]
>>> L.pop(  )                        # Pop off stack.
2
>>> L
[1]

Finally, because lists are mutable, you can also use the del statement to delete an item or section:

>>> L
['SPAM!', 'eat', 'more', 'please']
>>> del L[0]                       # Delete one item.
>>> L
['eat', 'more', 'please']
>>> del L[1:]                      # Delete an entire section.
>>> L                              # Same as L[1:] = [  ]
['eat']

Since slice assignment is a deletion plus an insert, you can also delete sections of lists by assigning an empty list to a slice (L[i:j]=[ ]); Python deletes the slice named on the left and then inserts nothing. Assigning an empty list to an index, on the other hand, just stores a reference to the empty list in the specified slot, rather than deleting it:

>>> L = ['Already', 'got', 'one']
>>> L[1:] = [  ]
>>> L
['Already']
>>> L[0] = [  ]
>>> L
[[  ]]

Here are a few pointers before moving on. Although all the operations above are typical, there are additional list methods and operations not illustrated here (including methods for inserting and searching). For a comprehensive and up-to-date list of type tools, you should always consult Python's manuals, or the Python Pocket Reference (O'Reilly) and other reference texts described in Preface.

We'd also like to remind you one more time that all the in-place change operations above work only for mutable objects: they won't work on strings (or tuples, discussed ahead), no matter how hard you try.

    [ Team LiB ] Previous Section Next Section