🚀 Supercharge your YouTube channel's growth with AI.
Try YTGrowAI FreePython sorted() to Sort Lists

In the tuple example, sorted(values) returns a new list and leaves the tuple in its original sequence. The different return from list.sort(), which changes a mutable list and returns None, caught my attention.
The key and reverse arguments control how the returned items are ordered. Their values are passed by name to the sorting call.
What sorted() returns from an iterable
sorted() is a Python built-in function that returns a new list of items in order. Its input is an iterable, which means an object you can read one item at a time, such as a list or tuple.
The call takes the input first, with optional key and reverse arguments supplied by name. Python’s sorted() reference defines the signature as sorted(iterable, /, *, key=None, reverse=False).
| Argument | What you supply | Default behavior |
|---|---|---|
| iterable | The collection or iterator to read | Required |
| key | A callable returning one comparison value per item | Compare the items themselves |
| reverse | True for descending order | False, ascending order |
The example below sorts a tuple into a new list, then calls list.sort() on a mutable list to compare the returned values. After assigning sorted(values) to ordered, you can print or iterate ordered while values keeps its original tuple sequence.
values = (8, 3, 5)
ordered = sorted(values)
print(ordered)
print(type(ordered).__name__)
print(values)
mutable_values = [8, 3, 5]
result = mutable_values.sort()
print(mutable_values)
print(result)
[3, 5, 8]
list
(8, 3, 5)
[3, 5, 8]
None
I got None back from list.sort(), even though mutable_values had changed order. Assign the return value from sorted() when you need a separate list, or call list.sort() on its own when you intend to rearrange the existing list. Work with the returned list to read the ordered items without rearranging the source tuple.
A new outer list still holds references to the same contained objects. Dictionaries inside it remain shared. Generator inputs also get consumed as sorted() reads them, which limits what “preserve the source” means for a one-use iterator.
These examples ran on Python 3.14.7 with the standard library only, and familiarity with Python lists and function calls is enough to follow the sorting decisions.
Step 1: Sort numbers in either direction
For numeric values, call sorted() without a key to compare the numbers directly. Ascending order puts the smallest value first, and repeated values stay in the result.
Set reverse=True when you want the largest value first. Both calls below read numbers into separate lists, leaving numbers available in its original order.
numbers = [8, 3, 5, 3]
print("Ascending:", sorted(numbers))
print("Descending:", sorted(numbers, reverse=True))
print("Original:", numbers)

The results are [3, 3, 5, 8] and [8, 5, 3, 3], but the original remains [8, 3, 5, 3]. Assign each return value to a different variable when later code needs both ordered views.
Reversing a list flips its existing sequence, so reversing unsorted numbers leaves them unsorted. Use reverse=True to choose a descending comparison order.
Step 2: Choose how strings compare
Python compares strings lexicographically, using Unicode code points to decide the first position where they differ. Uppercase letters can therefore appear before lowercase letters even when that order is awkward for a case-insensitive name list.
Pass str.casefold as the key to compare case-folded text and return the original spelling. For a length-based task, len supplies the number of characters instead of the string itself.
labels = ["python", "Python", "PYTHON", "go"]
print("Default:", sorted(labels))
print("Ignore case:", sorted(labels, key=str.casefold))
print("By length:", sorted(labels, key=len))
Default: ['PYTHON', 'Python', 'go', 'python']
Ignore case: ['go', 'python', 'Python', 'PYTHON']
By length: ['go', 'python', 'Python', 'PYTHON']
Length sorting happens to give the same result as case folding here because the Python spellings have equal lengths as well as equal case-folded keys. Their original relative order survives both sorts.
| Your task | Comparison key | Boundary |
|---|---|---|
| Ignore letter case | str.casefold | Does not supply locale-specific collation |
| Order by character count | len | Equal lengths retain input order |
| Apply locale-specific ordering | locale.strxfrm | Requires an appropriate configured locale |
Numeric text still follows character order unless you parse it. The names report10 and report2 sort in that order by default, so extract and convert the numeric suffix when it represents the ordering you need.
Step 3: Sort records by their score
A key function receives one input item and returns the value used for comparison. To order dictionary records by score, pass itemgetter(“score”) from the operator module, which selects that field from each record.
Mina appears before Jo in the sample input, and both have score 84, so their tie makes the ordering rule visible. Keep records available for the following tasks.
from operator import itemgetter
records = [
{"name": "Mina", "team": "blue", "score": 84},
{"name": "Ravi", "team": "red", "score": 91},
{"name": "Jo", "team": "blue", "score": 84},
{"name": "Bea", "team": "red", "score": 76},
]
by_score = sorted(records, key=itemgetter("score"))
descending = sorted(records, key=itemgetter("score"), reverse=True)
print([(row["name"], row["score"]) for row in by_score])
print([(row["name"], row["score"]) for row in descending])
[('Bea', 76), ('Mina', 84), ('Jo', 84), ('Ravi', 91)]
[('Ravi', 91), ('Mina', 84), ('Jo', 84), ('Bea', 76)]
Mina stays ahead of Jo in both directions. I expected descending scores, and the result kept that tied pair in its input order too, which is useful when you want score order without inventing a secondary ranking.

Stable sorting means equal comparison keys keep their original relative order. Python guarantees this for reverse=True as well, as the Sorting HOWTO explains.
The key runs once per input element, so it can compute a value without repeating that calculation for every comparison. I appended each record’s name inside a key function to see the calls. The equality test then compares its result with a lambda selecting the same score.
calls = []
def score_key(row):
calls.append(row["name"])
return row["score"]
ordered = sorted(records, key=score_key)
print(calls)
print(ordered == sorted(records, key=lambda row: row["score"]))
['Mina', 'Ravi', 'Jo', 'Bea']
True
The calls list contains each input name once. True confirms the same ordered result from the lambda. Pass the function itself as key, rather than calling it before sorted() has supplied an item.
Step 4: Break ties with a second field
A tuple key compares its fields from left to right, using the next field only when the preceding field ties. With itemgetter(“team”, “score”), team decides the group and score orders records inside that group.
by_team_and_score = sorted(records, key=itemgetter("team", "score"))
print([(row["team"], row["name"], row["score"])
for row in by_team_and_score])
[('blue', 'Mina', 84), ('blue', 'Jo', 84), ('red', 'Bea', 76), ('red', 'Ravi', 91)]
Mina and Jo tie on both fields, so the original order decides their position inside the blue group, followed by the red group in ascending score order.
Stable sorting preserves the earlier pass’s name order inside each tied score, so sort names before scores when you need descending scores with ascending names on a tie. The secondary field is sorted before the primary field.
by_name = sorted(records, key=itemgetter("name"))
by_score_desc = sorted(by_name, key=itemgetter("score"), reverse=True)
print([(row["name"], row["score"]) for row in by_score_desc])
[('Ravi', 91), ('Jo', 84), ('Mina', 84), ('Bea', 76)]
Jo now comes before Mina at score 84 because the name pass established that order. I would use stable passes when fields need different directions, rather than reversing a tuple key and accidentally reversing every field together.
Step 5: Sort dictionary entries by value
Iterating a dictionary supplies its keys, so sorted(scores) returns names in name order. Call scores.items() when you need each name to travel with its score, then compare the value at position 1 in each pair.
from operator import itemgetter
scores = {"Mina": 84, "Ravi": 91, "Jo": 84}
print("Keys:", sorted(scores))
print("Values:", sorted(scores.values()))
by_value = sorted(scores.items(), key=itemgetter(1))
print("Pairs:", by_value)
print("Dictionary:", dict(by_value))
Keys: ['Jo', 'Mina', 'Ravi']
Values: [84, 84, 91]
Pairs: [('Mina', 84), ('Jo', 84), ('Ravi', 91)]
Dictionary: {'Mina': 84, 'Jo': 84, 'Ravi': 91}
I got a list of names from sorted(scores), and the items() call returned name-score pairs with Mina ahead of Jo. Their scores tie, so the mapping’s insertion order decides their position.
| Input to sorted() | Returned elements | Use it when |
|---|---|---|
| scores | Keys | You need ordered names |
| scores.values() | Values | You only need ordered scores |
| scores.items() with key=itemgetter(1) | Key-value pairs | You need names alongside their sorted scores |
Passing the pair list to dict() creates a mapping in that insertion order. Python dictionaries preserve insertion order, but adding a new key later does not place it into score order, so sort again when you need a freshly ordered view.
Keep the pair list when your next operation iterates ordered entries. Build the dictionary only when the next operation needs lookups by name, since that container choice determines what later code can do directly.
When a sort key needs cleanup
Python cannot directly order an integer against a string, so mixed numeric text can raise TypeError before sorted() returns a list. The key must return comparable values even when the original items have different types.
Save this example as cleanup-demo.py and run python3 cleanup-demo.py to reproduce the rejected comparison and its numeric-key replacement. The exception is caught so execution continues to the call using int as the key.
values = [3, "4", "10"]
try:
print(sorted(values))
except TypeError as error:
print(type(error).__name__, str(error))
print("Numeric keys:", sorted(values, key=int))
![Python terminal showing TypeError for mixed strings and integers followed by numeric-key order [3, '4', '10']](/_e_/www.askpython.com/wp-content/uploads/2026/10/cleanup-demo.png)
I hit TypeError on the direct comparison, then got [3, “4”, “10”] with int as the key. The key changes the comparison value. The returned list still contains the original strings.
The conversion still needs valid numeric text. A currency symbol causes int to raise ValueError, so validate that field or normalize it before passing records into the sort.
A missing score field raises KeyError with itemgetter(“score”). To place missing or None scores after numeric scores in ascending order, use a tuple that compares the missing-value flag before the numeric value.
def optional_score_key(row):
score = row.get("score")
return (score is None, 0 if score is None else score)
optional_scores = [
{"name": "Desk", "score": 80},
{"name": "Lamp"},
{"name": "Zero", "score": 0},
{"name": "Chair", "score": 65},
]
ordered = sorted(optional_scores, key=optional_score_key)
print([(row["name"], row.get("score")) for row in ordered])
[('Zero', 0), ('Chair', 65), ('Desk', 80), ('Lamp', None)]
- The first tuple item is False for a present score and True for a missing score, so present scores come first.
- The second tuple item supplies the numeric score, including zero, or a comparable fallback for a missing score.
- With reverse=True, the missing-value flag also reverses, placing missing scores first. Keep ascending missing-value placement separate if that is not your intended policy.
The result puts Zero first and Lamp last, so zero is treated as a valid score rather than a missing value. This key assumes all present scores are numeric, and a string score still needs parsing before it can join those comparisons.
Keep the source when both orders matter
Changing a dictionary through the sorted result also changes the same record visible through the source list, because both lists refer to that dictionary. Only the outer list is new.
Try the shared-reference boundary with the same score fields. The descending list selects Ravi first, then changing ordered[0] changes the dictionary that source[1] also refers to.
source = [{"name": "Mina", "score": 84},
{"name": "Ravi", "score": 91}]
ordered = sorted(source, key=lambda row: row["score"], reverse=True)
ordered[0]["score"] = 0
print(source)
print(ordered[0] is source[1])
[{'name': 'Mina', 'score': 84}, {'name': 'Ravi', 'score': 0}]
True
I changed Ravi’s score through ordered and saw zero in source too. Keep these sorted views for read-only ordering, or copy the contained records separately when edits must be independent.
Other questions about sorted()
The remaining input questions concern consuming an iterator and turning sorted characters back into text, which affect whether you can reuse the input and how you handle the returned list.
Can I sort a generator more than once?
sorted() consumes the generator as it reads its items. Store the returned list or create a new generator before another sort, because an exhausted generator supplies no items.
How do I get a string back after sorting its characters?
sorted() returns a list of characters for a string input. Join the characters with an empty separator, as in ”.join(sorted(‘python’)), which returns ‘hnopty’.


