预计阅读本页时间:-
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 requiring that you make a new copy, as you had to for strings. Because Python deals only in object references, this distinction between changing an object in-place and creating a new object matters—as discussed in Chapter 6, if you change an object in-place, you might impact more than one reference to it at the same time.
Index and slice assignments
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
When using a list, you can change its contents by assigning to either 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 in Python 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 a combination of two steps:
- Deletion. The slice you specify to the left of the = is deleted.
- Insertion. The new items contained in the object to the right of the = are inserted into the list on the left, at the place where the old slice was deleted.[22]
This isn’t what really happens, 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 the 4 and 5 where the deleted 2 used to be. This also explains why L[1:2]=[] is really a deletion operation—Python deletes the slice (the item at offset 1), and then inserts nothing.
In effect, slice assignment replaces an entire section, or “column,” all at once. Because the length of the sequence being assigned does not have to match the length of the slice being assigned to, slice assignment can be used to replace (by overwriting), expand (by inserting), or shrink (by deleting) the subject list. It’s a powerful operation, but frankly, one that you may not see very often in practice. There are usually more straightforward ways to replace, insert, and delete (concatenation and the insert, pop, and remove list methods, for example), which Python programmers tend to prefer in practice.
List method calls
Like strings, Python list objects also support type-specific method calls, many of which change the subject list in-place:
>>> L.append('please') # Append method call: add item at end
>>> L
['eat', 'more', 'SPAM!', 'please']
>>> L.sort() # Sort list items ('S' < 'e')
>>> L
['SPAM!', 'eat', 'more', 'please']
Methods were introduced in Chapter 7. In brief, they are functions (really, attributes that reference functions) that are associated with particular objects. Methods provide type-specific tools; the list methods presented here, for instance, are generally available only for lists.
Perhaps the most commonly used list method is append, which 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 while the former changes L in-place, the latter makes a new list.[23]
Another commonly seen method, sort, orders a list in-place; it uses Python standard comparison tests (here, string comparisons), and by default sorts in ascending order. You can modify sort behavior by passing in keyword arguments—a special “name=value” syntax in function calls that specifies passing by name and is often used for giving configuration options. In sorts, the key argument gives a one-argument function that returns the value to be used in sorting, and the reverse argument allows sorts to be made in descending instead of ascending order:
>>> L = ['abc', 'ABD', 'aBe']
>>> L.sort() # Sort with mixed case
>>> L
['ABD', 'aBe', 'abc']
>>> L = ['abc', 'ABD', 'aBe']
>>> L.sort(key=str.lower) # Normalize to lowercase
>>> L
['abc', 'ABD', 'aBe']
>>>
>>> L = ['abc', 'ABD', 'aBe']
>>> L.sort(key=str.lower, reverse=True) # Change sort order
>>> L
['aBe', 'ABD', 'abc']
The sort key argument might also be useful when sorting lists of dictionaries, to pick out a sort key by indexing each dictionary. We’ll study dictionaries later in this chapter, and you’ll learn more about keyword function arguments in Part IV.
Note
Comparison and sorts in 3.0: In Python 2.6 and earlier, comparisons of differently typed objects (e.g., a string and a list) work—the language defines a fixed ordering among different types, which is deterministic, if not aesthetically pleasing. That is, the ordering is based on the names of the types involved: all integers are less than all strings, for example, because "int" is less than "str". Comparisons never automatically convert types, except when comparing numeric type objects.
In Python 3.0, this has changed: comparison of mixed types raises an exception instead of falling back on the fixed cross-type ordering. Because sorting uses comparisons internally, this means that [1, 2, 'spam'].sort() succeeds in Python 2.X but will raise an exception in Python 3.0 and later.
Python 3.0 also no longer supports passing in an arbitrary comparison function to sorts, to implement different orderings. The suggested workaround is to use the key=func keyword argument to code value transformations during the sort, and use the reverse=True keyword argument to change the sort order to descending. These were the typical uses of comparison functions in the past.
One warning here: 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.
Partly because of such constraints, sorting is also available in recent Pythons as a built-in function, which sorts any collection (not just lists) and returns a new list for the result (instead of in-place changes):
>>> L = ['abc', 'ABD', 'aBe']
>>> sorted(L, key=str.lower, reverse=True) # Sorting built-in
['aBe', 'ABD', 'abc']
>>> L = ['abc', 'ABD', 'aBe']
>>> sorted([x.lower() for x in L], reverse=True) # Pretransform items: differs!
['abe', 'abd', 'abc']
Notice the last example here—we can convert to lowercase prior to the sort with a list comprehension, but the result does not contain the original list’s values as it does with the key argument. The latter is applied temporarily during the sort, instead of changing the values to be sorted. As we move along, we’ll see contexts in which the sorted built-in can sometimes be more useful than the sort method.
Like strings, lists have other methods that perform other specialized operations. For instance, reverse reverses the list in-place, and the extend and pop methods insert multiple items at the end of and delete an item from the end of the list, respectively. There is also a reversed built-in function that works much like sorted, but it must be wrapped in a list call because it’s an iterator (more on iterators later):
>>> L = [1, 2]
>>> L.extend([3,4,5]) # Add many items at end
>>> L
[1, 2, 3, 4, 5]
>>> L.pop() # Delete and return last item
5
>>> L
[1, 2, 3, 4]
>>> L.reverse() # In-place reversal method
>>> L
[4, 3, 2, 1]
>>> list(reversed(L)) # Reversal built-in with a result
[1, 2, 3, 4]
In some types of programs, the list pop method used here is often used in conjunction with append to implement a quick last-in-first-out (LIFO) 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]
The pop method also accepts an optional offset of the item to be deleted and returned (the default is the last item). Other list methods remove an item by value (remove), insert an item at an offset (insert), search for an item’s offset (index), and more:
>>> L = ['spam', 'eggs', 'ham']
>>> L.index('eggs') # Index of an object
1
>>> L.insert(1, 'toast') # Insert at position
>>> L
['spam', 'toast', 'eggs', 'ham']
>>> L.remove('eggs') # Delete by value
>>> L
['spam', 'toast', 'ham']
>>> L.pop(1) # Delete by position
'toast'
>>> L
['spam', 'ham']
See other documentation sources or experiment with these calls interactively on your own to learn more about list methods.
Other common list operations
Because lists are mutable, you can use the del statement to delete an item or section in-place:
>>> 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']
Because slice assignment is a deletion plus an insertion, you can also delete a section of a list 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
[[]]
Although all the operations just discussed 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, Python’s dir and help functions (which we first met in Chapter 4), or one of the reference texts mentioned in the Preface.
I’d also like to remind you one more time that all the in-place change operations discussed here work only for mutable objects: they won’t work on strings (or tuples, discussed in Chapter 9), no matter how hard you try. Mutability is an inherent property of each object type.
[22] This description needs elaboration when the value and the 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.
[23] Unlike + concatenation, append doesn’t have to generate new objects, so it’s 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.