If you want to learn how to work with the sort()
method in your Python projects, then this article is for you. This method is very powerful and you can customize it to fit your needs, so let’s see how it works in detail.
You will learn:
sorted()
function.sort()
method performs a stable sort.Are you ready? Let’s begin! ⭐
With the sort()
method, you can sort a list in either:
Particularly, this method is used to sort a list in place, which means that it mutates it or modifies it directly without creating additional copies, so remember:
You will learn more about mutation in this article (I promise!) but for now it’s very important that you know that the sort()
method modifies the list, so its original version is lost permanently in the program.
Because of this, you should only use this method if:
If this fits your needs, then the .sort()
method is exactly what you are looking for.
Let’s see how you can call .sort()
to take advantage of its full power.
This is the most basic call (with no arguments):
If you don’t pass any arguments, by default:
<
operator.For example:
>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort()
>>> b
[2, 3, 3, 6, 7, 8, 9] # Sorted!
To customize how the sort()
method works, you can pass two optional arguments:
Let’s see how they change the behavior of this method. Here we have a method call with these two arguments:
Before explaining how they work, I would like to explain something that you probably noticed in the diagram (above): in the method call, the names of the parameters have to be included before their corresponding values, like this:
key=<f>
reverse=<value>
This is because they are keyword-only arguments. If you are passing a custom value for them, their names have to be specified in the method call, followed by an equal sign =
and their corresponding values, like this:
Otherwise, if you try to pass the arguments directly as we normally do for positional parameters, you will see this error because the function will not know which argument corresponds to which parameter:
TypeError: sort() takes no positional arguments
Now that you now what keyword-only arguments are, let’s start with reverse
.
The value of reverse
can be either True
or False
:
False
means that the list will be sorted in ascending order.True
means that the list will be sorted in descending (reverse) order.💡 Tip: By default, its value is False
if you don’t pass any arguments for this parameter, so the list is sorted in ascending order.
Here we have a few examples:
By default, reverse is False
# List of Integers
>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort()
>>> b
[2, 3, 3, 6, 7, 8, 9]
# List of Strings
>>> c = ["A", "Z", "D", "T", "U"]
>>> c.sort()
>>> c
['A', 'D', 'T', 'U', 'Z']
💡 Tip: If the elements of the list are strings, they are sorted alphabetically.
To specify that reverse is True, so the list has to be sorted in descending (reverse) order.
# List of Integers
>>> b = [6, 3, 8, 2, 7, 3, 9]
>>> b.sort(reverse=True)
>>> b
[9, 8, 7, 6, 3, 3, 2]
# List of Strings
>>> c = ["A", "Z", "D", "T", "U"]
>>> c.sort(reverse=True)
>>> c
['Z', 'U', 'T', 'D', 'A']
💡 Tip: Notice how the list is sorted in descending order if reverse
is True
.
Now that you know how to work with the reverse
parameter, let’s see the key
parameter.
This parameter is a little bit more detailed because it determines how the elements of the list are be compared during the sorting process.
Basic Syntax
The value of key
is either:
None
, which means that the elements of the list will be compared directly. For example, in a list of integers, the integers themselves can be used for the comparison.💡 Tip: By default, the value of key
is None
, so the elements are compared directly.
For example:
Let’s say that we want to sort a list of strings based on their length, from the shortest string to the longest string. We can pass the function len
as the value of key
, like this:
>>> d = ["aaa", "bb", "c"]
>>> d.sort(key=len)
>>> d
['c', 'bb', 'aaa']
💡 Tip: Notice that we are only passing the name of the function (len
) without parenthesis because we are not calling the function. This is very important.
Notice the difference between comparing the elements directly and comparing their length (see below). Using the default value of key
(None
) would have sorted the strings alphabetically (left) but now we are sorting them based on their length (right):
What happens behind the scenes? Each element is passed as argument to the len()
function and the value returned by this function call is used to perform the comparisons during the sorting process:
This results in a list with a different sorting criteria: length.
Here we have another example:
Another interesting example is sorting a list of strings as if they were all written in lowercase letters (for example, make “Aa” equivalent to “aa”).
According to lexicographical order, capital letters come before lowercase letters:
>>> "E" < "e"
True
So the string "Emma"
would come before "emily"
in a sorted list, even if their lowercase versions would be in the opposite order:
>>> "Emma" < "emily"
True
>>> "emma" < "emily"
False
To avoid distinguishing between capital and lowercase letters, we can pass the function str.lower
as key
. This will generate a lowercase version of the strings that will be used for the comparisons:
>>> e = ["Emma", "emily", "Amy", "Jason"]
>>> e.sort(key=str.lower)
>>> e
['Amy', 'emily', 'Emma', 'Jason']
Notice that now, "emily"
comes before "Emma"
in the sorted list, which is exactly what we wanted.
💡 Tip: if we had used the default sorting process, all the strings that started with an uppercase letter would have come before all the strings that started with a lowercase letter:
>>> e = ["Emma", "emily", "Amy", "Jason"]
>>> e.sort()
>>> e
['Amy', 'Emma', 'Jason', 'emily']
And… This is an example using Object-Oriented Programming (OOP):
If we have this very simple Python class:
>>> class Client:
def __init__(self, age):
self.age = age
And we create four instances:
>>> client1 = Client(67)
>>> client2 = Client(23)
>>> client3 = Client(13)
>>> client4 = Client(35)
We can make a list that references them:
>>> clients = [client1, client2, client3, client4]
Then, if we define a function to get the age
of these instances:
>>> def get_age(client):
return client.age
We can sort the list based on their age, passing the function as argument:
>>> clients.sort(key=get_age)
This is the final, sorted version of the list. We use a for loop to print the age of the instances in the order that they appear in the list:
>>> for client in clients:
print(client.age)
13
23
35
67
Exactly what we wanted, now the list is sorted in ascending order based on the age of the instances.
💡 Tip: Instead of defining a get_age
function, we could have used a lambda function to get the age of each instance, like this:
>>> clients.sort(key=lambda x: x.age)
Lambda functions are small and simple anonymous functions, which means that they don’t have a name. They are very helpful for these scenarios, when we only want to use them in particular places for a very short period of time.
This is the basic structure of the lambda function that we are using to sort the list:
Basic Structure of a Lambda Function
Awesome! Now you know to customize the functionality of the sort()
method, but you can take your skills to a whole new level by combining the effect of key
and reverse
in the same method call:
>>> f = ["A", "a", "B", "b", "C", "c"]
>>> f.sort(key=str.lower, reverse=True)
>>> f
['C', 'c', 'B', 'b', 'A', 'a']
Sort the list in reverse order as if the strings were in all lowercase.
These are the different combinations of the arguments and their effect:
Since we are specifying the names of the arguments, we already know which value corresponds to which parameter, so we can include either key
or reverse
first in the list and the effect will be exactly the same.
So this method call:
Is equivalent to:
This is an example:
>>> a = ["Zz", "c", "y", "o", "F"]
>>> a.sort(key=str.lower, reverse=True)
>>> a
['Zz', 'y', 'o', 'F', 'c']
If we change the order of the arguments, we get the exact same result:
>>> a = ["Zz", "c", "y", "o", "F"]
>>> a.sort(reverse=True, key=str.lower)
>>> a
['Zz', 'y', 'o', 'F', 'c']
Now let’s talk a little bit about the return value of this method. The sort()
method returns None
, it does not return a sorted version of the list, like we might intuitively expect.
According to the Python Documentation:
To remind users that it operates by side effect, it does not return the sorted sequence.
Basically, this is used to remind us that we are modifying the original list in memory, not generating a new copy of the list.
This is an example of the return value of sort()
:
>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
# Assign the return value to this variable:
>>> val = nums.sort()
# Check the return value:
>>> print(val)
None
See? None
was returned by the method call.
💡 Tip: It is very important not to confuse the sort()
method with the sorted()
function, which is a function that works very similarly but doesn’t modify the original list (it generates and returns a new copy of the list, already sorted).
This is an example that we can use to compare them:
# The sort() method returns None
>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
>>> val = nums.sort()
>>> print(val)
None
Example of .sort()
# sorted() returns a new list, a sorted copy of the original list
>>> nums = [6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
>>> val = sorted(nums)
>>> val
[2.4, 2.6, 3.5, 6.5, 7.3, 7.4]
# But it doesn't modify the original list
>>> nums
[6.5, 2.4, 7.3, 3.5, 2.6, 7.4]
Example of sorted()
This is very important because their effect is very different. Using the sort()
method when you intended to use sorted()
can introduce serious bugs to your program because you might not realize that the list is being mutated.
Now let’s talk a little bit about the characteristics of the sorting algorithm used by sort()
.
This method performs a stable sort because it works with an implementation of TimSort, a very efficient and stable sorting algorithm.
According to the Python Documentation:
A sort is stable if it guarantees not to change the relative order of elements that compare equal — this is helpful for sorting in multiple passes (for example, sort by department, then by salary grade).
This means that if two elements have the same value or intermediate value (key), they are guaranteed to stay in the same order relative to each other.
Let’s see what I mean with this. Please take a look at this example for a few moments:
>>> d = ["BB", "AA", "CC", "A", "B", "AAA", "BBB"]
>>> d.sort(key=len)
>>> d
['A', 'B', 'BB', 'AA', 'CC', 'AAA', 'BBB']
We are comparing the elements based on their length because we passed the len
function as the argument for key
.
We can see that there are three elements with length 2: "BB"
, "AA"
, and "CC"
in that order.
Now, notice that these three elements are in the same relative order in the final sorted list:
This is because the algorithm is guaranteed to be stable and the three of them had the same intermediate value (key) during the sorting process (their length was 2, so their key was 2).
💡 Tip: The same happened with "A"
and "B"
(length 1) and "AAA"
and "BBB"
(length 3), their original order relative to each other was preserved.
Now you know how the sort()
method works, so let’s dive into mutation and how it can affect your program.
As promised, let’s see how the process of mutation works behind the scenes:
When you define a list in Python, like this:
a = [1, 2, 3, 4]
You create an object at a specific memory location. This location is called the “memory address” of the object, represented by a unique integer called an id.
You can think of an id as a “tag” used to identify a specific place in memory:
You can access a list’s id using the id()
function, passing the list as argument:
>>> a = [1, 2, 3, 4]
>>> id(a)
60501512
When you mutate the list, you change it directly in memory. You may ask, why is this so risky?
It’s risky because it affects every single line of code that uses the list after the mutation, so you may be writing code to work with a list that is completely different from the actual list that exists in memory after the mutation.
This is why you need to be very careful with methods that cause mutation.
In particular, the sort()
method mutates the list. This is an example of its effect:
Here is an example:
# Define a list
>>> a = [7, 3, 5, 1]
# Check its id
>>> id(a)
67091624
# Sort the list using .sort()
>>> a.sort()
# Check its id (it's the same, so the list is the same object in memory)
>>> id(a)
67091624
# Now the list is sorted. It has been mutated!
>>> a
[1, 3, 5, 7]
The list was mutated after calling .sort()
.
Every single line of code that works with list a
after the mutation has occurred will use the new, sorted version of the list. If this was not what you intended, you may not realize that other parts of your program are working with the new version of the list.
Here is another example of the risks of mutation within a function:
# List
>>> a = [7, 3, 5, 1]
# Function that prints the elements of the list in ascending order.
>>> def print_sorted(x):
x.sort()
for elem in x:
print(elem)
# Call the function passing 'a' as argument
>>> print_sorted(a)
1
3
5
7
# Oops! The original list was mutated.
>>> a
[1, 3, 5, 7]
The list a
that was passed as argument was mutated, even if that wasn’t what you intended when you initially wrote the function.
💡 Tip: If a function mutates an argument, it should be clearly stated to avoid this sort of problems that could definitely introduce bugs in your program.
sort()
method lets you sort a list in ascending or descending order.key
and reverse
.reverse
determines if the list is sorted in ascending or descending order.key
is a function that generates an intermediate value for each element, and this value will be used to do the comparisons during the sorting process.sort()
method mutates the list, causing permanent changes. You need to be very careful and only use it if you will not need the original version of the list.Originally published by Estefania Cassingena Navone at https://www.freecodecamp.org
Python Tutorial: Sorting Lists, Tuples, and Objects
Sorting in Python || Learn Python Programming (Computer Science)
Python Tutorial for Beginners | Bubble Sort in python | List Sort
#python #web-development