Consider having a list of objects defined like this:
class MyItem:
def __init__(self):
self.count = 0
def increase(self):
self.count += 1
def decrease(self):
self.count -=1
def get_count(self):
return self.count
all_objects = []
for i in range(10):
all_objects.append(MyItem())
At various points in the program, the methods increase
and decrease
are called. Now you want to find the item which has the biggest count, but the max
function doesn't know the details of your object's implementation. Therefore, you need to let it know which function to use to evaluate the items between themself. In this case, this would be the item's get_count
method, and you would call the max
like this:
max(all_objects, key=lambda x: x.get_count)
Basically, a key
is the "reasoner" for the sorting, a value that is used for comparison.
To understand it better, consider that every call to a list of integers like this:
some_list = [1,2,3]
max(some_list) # returns 3
is the same as providing it a key as an unmodified value:
some_list = [1,2,3]
max(some_list, key=lambda x:x)
Update
Author has asked:
Could i incorporate booleans into the key element?
so I am extending my answer with the reply.
In short, the answer is: yes. But in practice, this has little to no use. As for explanation, consider the following case:
my_list = [True, False, False, True, False, False, False]
max(my_list)
The result will obviously be True
, since it is convention that True
>False
. But the question is, which True
object is returned (remember that everything is an object in Python, even boolean types)? Is it the first one or the second one? Well, that depends on the internal implementation of ṁax
. Since this is so, we don't have practical use of this application. (Incidentally, max
is implemented to return the first one, but this doesn't concern us at this time.)
Important note: The above example is a simplification that assumes that two True
object are in fact two different object, which is not correct. In reality
they are the same object in Python, as noted by @mu無 in the comments. Consider this as a lumpy
example, and for a more relevant one, read on.
But lets check how this behaves with our previous example:
class MyItem:
def __init__(self, id):
self.count = 0
self._bool = bool(random.randrange(2))
self.id = id
def increase(self):
self.count += 1
def decrease(self):
self.count -=1
def get_count(self):
return self.count
def get_bool(self):
return self._bool
We have added only three things, the id
to identify the object, the private attribute _bool
and the getter function get_bool
to be used as the key
. Lets create a list of such objects and do some random incrementing to differentiate between them:
import random
all_objects = []
for i in range(10):
new_obj = MyItem(i)
for k in range(random.randrange(10)):
new_obj.increase()
all_objects.append(new_obj)
At this point, we have a list all_objects
containing 10 MyItem
objects with random values in their count
attribute, and a random boolean
value in their _bool
attribute.
Now lets see what happens when we try to sort that. We will first print all of them, so its easier to make a conclusion. I will show three consecutive results as columns to preserve space.
# a helper function to print formatted output
def print_object(obj):
print "id: {0} count: {1} _bool: {2}".format(o.id, o.get_count(), o.get_bool())
# print all objects followed by delimited line
# for the object returned by max
for o in all_objects: print_object(o)
print "-"*27
max_obj = max(all_objects, key=lambda x:x.get_bool())
print_object(max_obj)
The output:
id: 0 count: 1 _bool: False id: 0 count: 2 _bool: False id: 0 count: 1 _bool: False
id: 1 count: 7 _bool: True id: 1 count: 3 _bool: False id: 1 count: 4 _bool: False
id: 2 count: 0 _bool: False id: 2 count: 1 _bool: False id: 2 count: 2 _bool: False
id: 3 count: 5 _bool: False id: 3 count: 4 _bool: False id: 3 count: 1 _bool: True
id: 4 count: 4 _bool: False id: 4 count: 6 _bool: False id: 4 count: 9 _bool: False
id: 5 count: 4 _bool: False id: 5 count: 6 _bool: False id: 5 count: 3 _bool: False
id: 6 count: 7 _bool: True id: 6 count: 4 _bool: False id: 6 count: 5 _bool: False
id: 7 count: 8 _bool: True id: 7 count: 7 _bool: True id: 7 count: 1 _bool: True
id: 8 count: 1 _bool: True id: 8 count: 8 _bool: False id: 8 count: 9 _bool: False
id: 9 count: 7 _bool: True id: 9 count: 4 _bool: False id: 9 count: 1 _bool: False
--------------------------- --------------------------- ---------------------------
id: 1 count: 7 _bool: True id: 7 count: 7 _bool: True id: 3 count: 1 _bool: True
From the output we can clearly see that the function returned the first item with the _bool
attribute containing the value True
. Which is hardly max
of anything important...