<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
Step2:  Bonus1
Step4:  Bonus2
Step6:  Bonus3
Step8:  We may use lambda when no key is provided like so
Step10:  Unit Tests
<ASSISTANT_TASK:>
Python Code:
multimax([])
def multimax(iterable):
     Return a list of all maximum values 
    try:
        max_item = max(iterable)
    except ValueError:
        return []
    
    return [
        item
        for item in iterable
        if item == max_item
    ]
multimax([])
def multimax(iterable):
     Return a list of all maximum values 
    max_item = max(iterable, default=None) # Using the default keyword-only argument of max prevents exception.
    
    return [
        item
        for item in iterable
        if item == max_item
    ]
multimax([])
Explanation: Bonus1: multimax function returns an empty list if the given iterable is empty
End of explanation
numbers = [1, 3, 8, 5, 4, 10, 6]
odds = (n for n in numbers if n % 2 == 1)
multimax(odds)
def multimax(iterable):
     Return a list of all maximum values 
    maximums = []
    
    for item in iterable:
        if not maximums or maximums[0] == item:
            maximums.append(item)
        else:
            if item > maximums[0]:
                maximums = [item]
                
    return maximums            
multimax([])
multimax([1, 4, 2, 4, 3])
numbers = [1, 3, 8, 5, 4, 10, 6]
odds = (n for n in numbers if n % 2 == 1)
multimax(odds)
Explanation: Bonus2: multimax function will work with iterators (lazy iterables) such as files, zip objects, and generators
End of explanation
def multimax(iterable, key=None):
     Return a list of all maximum values 
    if key is None:
        def key(item): return item
        
    maximums = []
    key_max = None
    
    for item in iterable:
        k = key(item)
        
        if k == key_max:
            maximums.append(item)
        elif not maximums or k > key_max:
            key_max = k
            maximums = [item]          
            
    return maximums        
multimax([1, 2, 4, 3])
multimax([1, 4, 2, 4, 3])
numbers = [1, 3, 8, 5, 4, 10, 6]
odds = (n for n in numbers if n % 2 == 1)
multimax(odds)
multimax([])
words = ["cheese", "shop", "ministry", "of", "silly", "walks", "argument", "clinic"]
multimax(words, key=len)
Explanation: Bonus3: multimax function accept a keyword argument called "key" that is a function which will be used to determine the key by which to compare values as maximums
End of explanation
def multimax(iterable, key=lambda x: x):
     Return a list of all maximum values 
    maximums = []
    key_max = None
    
    for item in iterable:
        k = key(item)
        
        if k == key_max:
            maximums.append(item)
        elif not maximums or k > key_max:
            key_max = k
            maximums = [item]          
            
    return maximums        
Explanation: We may use lambda when no key is provided like so:
End of explanation
import unittest
class MultiMaxTests(unittest.TestCase):
    Tests for multimax.
    def test_single_max(self):
        self.assertEqual(multimax([1, 2, 4, 3]), [4])
    def test_two_max(self):
        self.assertEqual(multimax([1, 4, 2, 4, 3]), [4, 4])
    def test_all_max(self):
        self.assertEqual(multimax([1, 1, 1, 1, 1]), [1, 1, 1, 1, 1])
    def test_lists(self):
        inputs = [[0], [1], [], [0, 1], [1]]
        expected = [[1], [1]]
        self.assertEqual(multimax(inputs), expected)
    def test_order_maintained(self):
        inputs = [
            (3, 2),
            (2, 1),
            (3, 2),
            (2, 0),
            (3, 2),
        ]
        expected = [
            inputs[0],
            inputs[2],
            inputs[4],
        ]
        outputs = multimax(inputs)
        self.assertEqual(outputs, expected)
        self.assertIs(outputs[0], expected[0])
        self.assertIs(outputs[1], expected[1])
        self.assertIs(outputs[2], expected[2])
    # To test the Bonus part of this exercise, comment out the following line
    # @unittest.expectedFailure
    def test_empty(self):
        self.assertEqual(multimax([]), [])
    # To test the Bonus part of this exercise, comment out the following line
    # @unittest.expectedFailure
    def test_iterator(self):
        numbers = [1, 4, 2, 4, 3]
        squares = (n**2 for n in numbers)
        self.assertEqual(multimax(squares), [16, 16])
    # To test the Bonus part of this exercise, comment out the following line
    # @unittest.expectedFailure
    def test_key_function(self):
        words = ["alligator", "animal", "apple", "artichoke", "avalanche"]
        outputs = ["alligator", "artichoke", "avalanche"]
        self.assertEqual(multimax(words, key=len), outputs)
if __name__ == "__main__":
    unittest.main(argv=['first-arg-is-ignored'], exit=False)
Explanation: Unit Tests
End of explanation
<END_TASK>