You'd adopt a binary search to find the right Any
object for your attr_a
value. The bisect
module provides a starting point:
def bisect_left(a, x, lo=0, hi=None, key=None):
if key is None: key = lambda v: v
if lo < 0:
raise ValueError('lo must be non-negative')
if hi is None:
hi = len(a)
while lo < hi:
mid = (lo+hi)//2
if key(a[mid]) < x: lo = mid+1
else: hi = mid
return lo
The only thing I did was add a key
function to the signature here. key
takes a callable that returns the value against we are bisecting.
Now you can use bisection to find your Any
index:
from operator import attrgetter
index = bisect_left(L, x, key=attrgetter('attr_a'))
This returns either the index of a matching Any
, or the index of the next Any
object whose attr_a
value is higher than x
. You may need to test and/or adjust the algorithm for those cases. For example, you could verify that attr_a
does indeed match the desired value:
def find_by_x(L, x, key):
index = bisect_left(L, x, key=key)
if key(L[index]) != x:
raise IndexError('{} not found'.format(x))
return L[index]
Demo:
>>> from operator import attrgetter
>>> L = [Any(-3, 4), Any(-2, 1), Any(0, 2), Any(2, 1), Any(5, 6), Any(6, 3), Any(8, 2), Any(10, 1), Any(13, 5), Any(14, 3)]
>>> x = 6
>>> bisect_left(L, x, key=attrgetter('attr_a'))
5
>>> L[bisect_left(L, x, key=attrgetter('attr_a'))].attr_b
3
>>> find_by_x(L, x, key=attrgetter('attr_a')).attr_b
3
>>> x = 12
>>> find_by_x(L, x, key=attrgetter('attr_a')).attr_b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in find_by_x
IndexError: 12 not found