Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda function error while using python3

I have a piece of code that works in Python 2.7 but not in Python3.7. Here I am trying to sort by values of a lambda function.

def get_nearest_available_slot(self):
    """Method to find nearest availability slot in parking
    """
    available_slots = filter(lambda x: x.availability, self.slots.values())
    if not available_slots:
        return None
    return sorted(available_slots, key=lambda x: x.slotNum)[0]

The error I get is:

File "/home/xyz/Desktop/parking-lot/parking-lot-1.4.2/parking_lot/bin/source/parking.py", line 45, in get_nearest_available_slot
    return sorted(available_slots, key=lambda x: x.slotNum)[0]
IndexError: list index out of range

What am I doing wrong here?

like image 406
Ajay Singh Avatar asked Jan 26 '23 05:01

Ajay Singh


1 Answers

The answer is simple: it's because of how filter works.

In Python 2, filter is eagerly evaluated, which means that once you call it, it returns a list:

filter(lambda x: x % 2 == 0, [1, 2, 3])

Output:

[2]

Conversely, in Python 3, filter is lazily evaluated; it produces an object you can iterate over once, or an iterator:

<filter at 0x110848f98>

In Python 2, the line if not available_slots stops execution if the result of filter is empty, since an empty list evaluates to False.

However, in Python 3, filter returns an iterator, which always evaluates to True, since you cannot tell if an iterator has been exhausted without trying to get the next element, and an iterator has no length. See this for more information.

Because of this, a case exists where an empty iterator gets passed to sorted, producing another empty list. You cannot access the element at position 0 of an empty list, so you get an IndexError.

To fix this, I suggest evaluating the condition strictly. You could do something like this, replacing sorted with min, since we only need one value:

def get_nearest_available_slot(self):
    """Method to find nearest availability slot in parking
    """
    available_slots = [slot for slot in self.slots.values() if slot.availability]
    if available_slots:
        return min(available_slots, key=lambda x: x.slotNum)

    else:
        return None
like image 111
gmds Avatar answered Jan 28 '23 18:01

gmds