During my recent work on a mathematical problem in Python, I encountered some confusion regarding the behavior of the bisect.bisect_left
function from the bisect library. This confusion arose due to its similarity with another function in the library, namely bisect.insort_left
.
In my specific use case, I was utilizing a custom key function, such as bisect.insort_left(my_list, my_item, key=my_key)
, which behaved as expected. This function determined the appropriate index for my_item
in my_list
using the specified key and inserted it accordingly.
However, when attempting a similar operation with bisect.bisect_left(my_list, my_item, key=my_key)
, I encountered an unexpected TypeError: "other argument must be K instance". This error message lacked clarity regarding the underlying issue.
Upon investigating the source code of bisect, I discovered the correct usage pattern, as indicated by line 71 of the source code. It became apparent that the correct usage involves calling the key function with the item as an argument, like so: bisect.bisect_left(my_list, my_key(my_item), key=my_key)
.
I am curious about the design decision behind this requirement. Why is it necessary to call my_key(my_item)
when using bisect.bisect_left
compared to the more straightforward usage in bisect.insort_left
?
Let's say the list items contain a lot of data. Each item can be e. g. a dict
{
'id': 12,
'firstname': 'foo',
'lastname': 'bar',
'address': ...,
'phone': ...
...
}
These items are sorted in the list by id
with an appropriate key function like lambda item: item['id']
.
If you want to find an item with bisect_left
it shouldn't be necessary to provide the whole item with all of its data but the id should be enough.
In the docs this is mentioned a bit cryptically:
To support searching complex records, the key function is not applied to the x value.
If you want to insert an item with insort_left
of course you have to provide the complete item with all data.