Sorting, Slicing, and the Methods That Return Nothing
Sorting a list of names is one function call, right up until the names have capital letters in them and the answer is quietly wrong. Sort the same list four ways, changing only the key, and watch the order rearrange itself under each rule.
Worth reading first: Ordered Collections, Two Ways
Sorting a list of names is one function call, right up until the names have capital letters in them and the answer is quietly wrong. Sort the same list four ways, changing only the key, and watch the order rearrange itself under each rule.
sorted builds a new list; sort rewrites yours
You met this pair briefly when lists first appeared. It is worth returning to, because the difference between them is the difference between a function and a method, and Python spells it out in what each one hands back.
>>> scores = [88, 61, 94]>>> sorted(scores)[61, 88, 94]>>> scores[88, 61, 94] >>> scores.sort()>>> scores[61, 88, 94]sorted() left the original alone and returned a new list. .sort() returned nothing at all and rewrote the list in place. The missing return value is not an oversight — it is Python telling you, every time, that the work happened to the thing you called it on.
The key argument is the whole feature
Both accept a key: a function applied to each item purely to decide where it goes. The items themselves are never altered, and it is the returned values that get compared, not what you can see.
This matters more than it sounds, because Python's default ordering for text is not alphabetical. It compares code points, and every capital letter sits below every lowercase one — so "Zoe" sorts before "ada" and nothing warns you.
>>> sorted(names)
Every capital sorts before every lowercase letter, because 'B' is code point 66 and 'a' is 97. This is not the alphabetical you meant.
reverse is not the same as reversed
Three similar-looking things, and only two of them are related.
Sorts, then hands back the order flipped. Still uses the same comparison, so a wrong ordering stays wrong, just backwards.
Reverses the list in place, with no sorting involved at all. Returns None, like sort().
Returns a lazy iterator walking backwards. Not a list — wrap it in list() if you want to see it more than once.
Assigning into a slice changes the length
You already know a slice reads a section out of a list. What is less obvious is that a slice can be assigned to — and when it is, the replacement does not have to be the same size as the section it replaces.
>>> letters = ["a", "b", "c", "d"]>>> letters[1:3] = ["X"]>>> letters['a', 'X', 'd'] >>> letters[1:2] = ["p", "q", "r"]>>> letters['a', 'p', 'q', 'r', 'd']Two items became one, and then one became three. The list grew and shrank without a single call to append or remove. This is a sharp tool: it is the neatest way to splice a section out of a list, and it is also a silent way to change a length you thought was fixed.
append adds one thing; extend adds each thing
These two are confused constantly, and the confusion only shows up when what you are adding happens to be iterable.
>>> a = [1, 2]>>> a.append([3, 4])>>> a[1, 2, [3, 4]] >>> b = [1, 2]>>> b.extend([3, 4])>>> b[1, 2, 3, 4]append(x)
Adds exactly one item, whatever it is. The list always grows by one, so a list of four appended to a list of two gives you three items, not six.
extend(x)
Walks whatever you gave it and adds each item separately. Hand it a string and you get one item per character — extend("hi") adds "h" and "i".
The multiplication trap in a list of lists
Multiplying a list repeats it, which is a convenient way to build a row of zeros. It becomes a trap the moment the thing being repeated is itself a list.
>>> grid = [[0] * 3] * 3>>> grid[[0, 0, 0], [0, 0, 0], [0, 0, 0]] >>> grid[0][0] = 1>>> grid[[1, 0, 0], [1, 0, 0], [1, 0, 0]]One assignment changed three rows, because there are not three rows. There is one row, listed three times. * 3 repeated the reference, not the list behind it — the same lesson names taught you, arriving in a shape that is far harder to spot.
Key takeaways
- sorted() returns a new list; .sort() returns None and rewrites the one you called it on. Never assign the result of .sort() to a name.
- Default text ordering compares code points, so every capital sorts before every lowercase letter. key=str.lower is what most people actually meant.
- A key function decides the order and nothing else — the items you get back are the originals, untouched.
- Python's sort is stable: items that compare equal keep the order they already had.
- Assigning into a slice can change the list's length, because the replacement need not be the same size as the section it replaces.
- append adds one item whatever it is; extend adds each item of what you gave it.
- [[0] * 3] * 3 makes one row referenced three times, not three rows. Use a comprehension when the repeated thing is mutable.
Quick check
Answer these to unlock the next chapter — 3 of 4 to pass. You can retake it anytime.
Answer every question to check.
Make a free account to read on
Every chapter is free — an account is how your progress, XP, and streak follow you from your laptop to your phone, and how you show up on the leaderboard. No payment, no trial.