3

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?

1
  • Add more details perhaps, for how does x.availability , self.slots etc look like Commented Jun 10, 2019 at 7:59

1 Answer 1

8

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
Sign up to request clarification or add additional context in comments.

1 Comment

nice clear explanation- an alternative would be removing the if clause and next(iter(sorted(available_slots, key=lambda x: x.slotNum)), None)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.