5

I am relatively new to Python and i have been going over the documentation for various built-in functions.

When it comes to max/min functions:

 `max(arg1, arg2, *args[, key])`  or `max(iterable[, key])`

I know that arg1, arg2 etc or iterable could be a sequence of random values, however what is the role of the "key" element? Can someone give me an example of its application?

Normally when i see this built-in function, i automatically imagine a random list say x = [1,2,3] and going max(x) would yield the value of 3. However, what could the "key" feature offer me to manipulate this function some other way than just going through a simple straightforward list?

I am new to Python and not really up to speed with all the jargon in Python Docs.

Thanks, Ed

user40720
  • 89
  • 1
  • 2
  • 9

4 Answers4

5

The key is used to pass a custom comparison function.

Example: output max by length of list, where arg1, arg2 are both lists.

>>> max([1,2,3,4], [3,4,5], key=len)
[1, 2, 3, 4]

Example: output max by sum of elements of args' lists

>>> max([1,2,3,4,5], [3,4,5], key=sum)
[1, 2, 3, 4, 5]

>>> max([1,2,3,4], [3,4,5], key=sum)
[3, 4, 5]

You can similarly use specific comparison functions for different arg objects.

Anshul Goyal
  • 73,278
  • 37
  • 149
  • 186
3

You can use it when your you want to find the max of a sequence and you'd like a specific definition of max.

For example, say I have a list of tuple. If I just use max without the key argument, it will by default use the first item in each tuple

>>> l = [(1,3), (2,4), (1,9), (4,1)]

>>> max(l)
(4, 1)

But what if I want the max from the list, but by considering the second element of the tuple?

>>> max(l, key = lambda i : i[1])
(1, 9)

# Or

>>> import operator
>>> max(l, key = operator.itemgetter(1))
(1, 9)

Also what about a list of strings, and you want to find the max as if they were the numeric values?

>>> l = ['4', '11', '6', '31']

Just using max will sort them lexicographically

>>> max(l)
'6'

But again I can use key

>>> max(l, key = lambda i: int(i))
'31'
Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • Great to mention `operator`! The last example can also be simplified into `max(l, key=int)`. Avoid lambdas, they are slow and difficult to debug. – uranusjr Jul 18 '18 at 07:41
1
>>> my_list = ["cat","dog","monkey","elephant","horse"]
>>> max(my_list,key=len)
'elephant'

so in here it will check which element has maximum length. so here key define on what parameter, max will select maximum value.

Key can be any built-in function or user defined function.

Hackaholic
  • 19,069
  • 5
  • 54
  • 72
1

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...

bosnjak
  • 8,424
  • 2
  • 21
  • 47
  • Could i incorporate booleans into the key element? – user40720 Dec 15 '14 at 17:58
  • Yes, you can, but there is no much use from it. I will update my answer with the details. – bosnjak Dec 15 '14 at 19:01
  • ` But the question is, which True object is returned` - This statement is incorrect, all `True` are the same `True` objects. Run this in your shell `l = [True, True]; id(id(l[0]) == id(l[1])) == id(l[0]) == id(l[1])` – Anshul Goyal Dec 16 '14 at 08:15
  • @mu無: ok, good point. I overlooked this fact, I was guided by the actual example I provided, with a custom object, so I overlooked to actually test this. I will edit my answer accordingly. Thanks! – bosnjak Dec 16 '14 at 08:19