Python - Sort List (sort vs sorted)
Python provides two built-in approaches for sorting: the `sort()` method and the `sorted()` function. The fundamental distinction lies in mutability and return values.
Key Insights
sort()modifies lists in-place and returns None, whilesorted()creates a new sorted list and works with any iterable- Both methods use Timsort algorithm with O(n log n) complexity and support custom sorting via
keyandreverseparameters - Choose
sort()for memory efficiency with large lists you don’t need to preserve,sorted()when you need the original data or are working with non-list iterables
Understanding the Core Difference
Python provides two built-in approaches for sorting: the sort() method and the sorted() function. The fundamental distinction lies in mutability and return values.
# sort() - modifies in place, returns None
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
result = numbers.sort()
print(numbers) # [1, 1, 2, 3, 4, 5, 6, 9]
print(result) # None
# sorted() - returns new list, original unchanged
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
result = sorted(numbers)
print(numbers) # [3, 1, 4, 1, 5, 9, 2, 6]
print(result) # [1, 1, 2, 3, 4, 5, 6, 9]
The sort() method only works on lists because it modifies the object directly. The sorted() function accepts any iterable and always returns a new list.
# sorted() works with any iterable
sorted_tuple = sorted((5, 2, 8, 1)) # [1, 2, 5, 8]
sorted_string = sorted("python") # ['h', 'n', 'o', 'p', 't', 'y']
sorted_dict = sorted({3: 'c', 1: 'a', 2: 'b'}) # [1, 2, 3]
# sort() only works on lists
my_tuple = (5, 2, 8, 1)
# my_tuple.sort() # AttributeError: 'tuple' object has no attribute 'sort'
Reverse Sorting
Both methods support reverse sorting through the reverse parameter.
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
# Descending order with sort()
numbers.sort(reverse=True)
print(numbers) # [9, 6, 5, 4, 3, 2, 1, 1]
# Descending order with sorted()
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
desc = sorted(numbers, reverse=True)
print(desc) # [9, 6, 5, 4, 3, 2, 1, 1]
Custom Sorting with Key Functions
The key parameter accepts a function that extracts a comparison key from each element. This is where sorting becomes powerful.
# Sort strings by length
words = ["python", "is", "awesome", "for", "data"]
words.sort(key=len)
print(words) # ['is', 'for', 'data', 'python', 'awesome']
# Sort case-insensitive
names = ["Alice", "bob", "Charlie", "david"]
sorted_names = sorted(names, key=str.lower)
print(sorted_names) # ['Alice', 'bob', 'Charlie', 'david']
# Sort by absolute value
integers = [-5, 2, -8, 1, 9, -3]
integers.sort(key=abs)
print(integers) # [1, 2, -3, -5, -8, 9]
Sorting Complex Data Structures
When working with dictionaries or objects, the key parameter becomes essential.
# Sort list of dictionaries
employees = [
{"name": "Alice", "age": 30, "salary": 70000},
{"name": "Bob", "age": 25, "salary": 50000},
{"name": "Charlie", "age": 35, "salary": 90000}
]
# Sort by age
employees.sort(key=lambda x: x["age"])
print([e["name"] for e in employees]) # ['Bob', 'Alice', 'Charlie']
# Sort by salary descending
sorted_by_salary = sorted(employees, key=lambda x: x["salary"], reverse=True)
print([e["name"] for e in sorted_by_salary]) # ['Charlie', 'Alice', 'Bob']
For custom objects, define comparison logic:
class Product:
def __init__(self, name, price, rating):
self.name = name
self.price = price
self.rating = rating
def __repr__(self):
return f"Product({self.name}, ${self.price}, {self.rating}★)"
products = [
Product("Laptop", 1200, 4.5),
Product("Mouse", 25, 4.8),
Product("Keyboard", 80, 4.3)
]
# Sort by price
products.sort(key=lambda p: p.price)
print(products)
# [Product(Mouse, $25, 4.8★), Product(Keyboard, $80, 4.3★), Product(Laptop, $1200, 4.5★)]
# Sort by rating descending
by_rating = sorted(products, key=lambda p: p.rating, reverse=True)
print(by_rating)
# [Product(Mouse, $25, 4.8★), Product(Laptop, $1200, 4.5★), Product(Keyboard, $80, 4.3★)]
Multi-Level Sorting
Sort by multiple criteria using tuples in the key function. Python compares tuples element-by-element.
students = [
("Alice", "A", 95),
("Bob", "B", 85),
("Charlie", "A", 92),
("David", "B", 88),
("Eve", "A", 95)
]
# Sort by grade, then score descending
students.sort(key=lambda x: (x[1], -x[2]))
print(students)
# [('Eve', 'A', 95), ('Alice', 'A', 95), ('Charlie', 'A', 92),
# ('David', 'B', 88), ('Bob', 'B', 85)]
For more complex scenarios, use operator.itemgetter or operator.attrgetter:
from operator import itemgetter, attrgetter
# Sort by multiple dictionary keys
data = [
{"dept": "Sales", "name": "Alice", "score": 95},
{"dept": "IT", "name": "Bob", "score": 88},
{"dept": "Sales", "name": "Charlie", "score": 92},
{"dept": "IT", "name": "David", "score": 91}
]
data.sort(key=itemgetter("dept", "score"), reverse=True)
print([(d["dept"], d["name"], d["score"]) for d in data])
# [('Sales', 'Alice', 95), ('Sales', 'Charlie', 92),
# ('IT', 'David', 91), ('IT', 'Bob', 88)]
Performance Considerations
Both methods use Timsort, optimized for real-world data with O(n log n) worst-case complexity. The difference lies in memory usage.
import sys
# Memory comparison
large_list = list(range(1000000, 0, -1))
original_size = sys.getsizeof(large_list)
# sort() - no additional list created
large_list.sort()
# Memory used: original_size
# sorted() - creates new list
large_list = list(range(1000000, 0, -1))
new_list = sorted(large_list)
# Memory used: original_size + sys.getsizeof(new_list)
For large datasets where you don’t need the original order, sort() is more memory-efficient. Use sorted() when you need to preserve the original or when working with immutable iterables.
Stability in Sorting
Both methods provide stable sorting—elements with equal keys maintain their relative order.
# Stable sort example
records = [
("Alice", 25),
("Bob", 30),
("Charlie", 25),
("David", 30)
]
# Sort by age - original order preserved for equal ages
sorted_records = sorted(records, key=lambda x: x[1])
print(sorted_records)
# [('Alice', 25), ('Charlie', 25), ('Bob', 30), ('David', 30)]
This stability is crucial when performing multiple sorts:
# Sort by secondary criterion first, then primary
employees = [
{"name": "Alice", "dept": "IT", "salary": 70000},
{"name": "Bob", "dept": "Sales", "salary": 70000},
{"name": "Charlie", "dept": "IT", "salary": 80000}
]
# First sort by name
employees.sort(key=lambda x: x["name"])
# Then sort by salary (stable, preserves name order within same salary)
employees.sort(key=lambda x: x["salary"])
print([(e["name"], e["salary"]) for e in employees])
# [('Alice', 70000), ('Bob', 70000), ('Charlie', 80000)]
Practical Decision Matrix
Use sort() when:
- Working exclusively with lists
- Memory efficiency is critical
- You don’t need the original order
- Modifying in-place is acceptable
Use sorted() when:
- Working with any iterable (tuples, strings, sets, generators)
- You need to preserve the original collection
- Creating intermediate sorted versions
- The function call style is more readable in your context
# Practical example: filtering and sorting
data = [15, 8, 23, 4, 42, 16, 11]
# Keep original, work with filtered sorted version
evens_sorted = sorted(x for x in data if x % 2 == 0)
print(f"Original: {data}") # [15, 8, 23, 4, 42, 16, 11]
print(f"Evens: {evens_sorted}") # [4, 8, 16, 42]
# Or modify in-place when original isn't needed
data = [x for x in data if x % 2 == 0]
data.sort()
print(f"Modified: {data}") # [4, 8, 16, 42]
Both methods are highly optimized and suitable for production use. Choose based on your specific requirements for mutability, memory usage, and data type compatibility.