On Fri, May 22, 2020 at 06:37:11PM -0700, Ethan Furman wrote:
> On 05/22/2020 05:11 PM, David Mertz wrote:
> >On 05/22/2020 04:43 AM, Steven D'Aprano wrote:
> 
> >>    i = somelist.index(needle, pred=comparison)
> 
> >Why not just this (by object, not by its index, but that seems simpler):
> >
> >  >>> do_something(next(filter(pred, somelist)))
> >  Something about 55
> >  >>> somelist
> >  [3, 4, 29, 23, 46, 55, 90, 81]
> >  >>> pred
> >  <function div5 at 0x7fd10794ee50>
> 
> Steven, using David's example list, what would `needle` and `comparison` be 
> in your proposal?

Good question! Thank you for pushing me to think about this a little 
harder.

I read David's example as "first item that is divisible by 5" so the 
predicate would be rubbish:

    # I can never remember if the needle comes first or second...
    somelist.index(0, pred=lambda a, b: a%5 == b)
    somelist.index(0, pred=lambda a, b: b%5 == a)

Yuck. So I think I have the wrong mental model here.

Going back to the thread on discuss that inspired the question, I think 
a *key function* is probably better:

    for index, blwr in enumerate(blowers):
        if blwr.id_ == value:
            print(index)
            break

    # becomes
    blowers.index(value, key=operator.attrgetter('id_'))

    # or if you prefer
    blowers.index(value, key=lambda obj: obj.id_)

although I expect the attrgetter version may be faster, for those who 
care about such things.

With a key function, David's example becomes:

    somelist.index(0, key=lambda n: n%5)


Here are a few more possibilities.

    # number not divisible by 5
    somelist.index(True, key=lambda x: bool(x%5))

    # case-insensitive search
    somelist.index('spam', key=str.casefold)

    # number less than 0
    somelist.index(True, key=lambda x: x<0)

    # item of length exactly 2
    somelist.index(2, key=len)

    # identity search
    somelist.index(True, key=lambda obj: obj is someobj)

    # find a non-NAN
    somelist.index(False, key=math.isnan)

    # find a particular user
    somelist.index('guido', key=lambda user: user.name)

    # instance of a class
    somelist.index(MyClass, key=type)  # doesn't match subclasses
    # does match subclasses
    somelist.index(True, key=lambda obj: isinstance(obj, MyClass))

    # z-coordinate of a 3D point equals -7
    somelist.index(-7, key=lambda point: point[2])

    # sum of values in the item equals 99
    somelist.index(99, key=sum)

    # sum of sales for the month is greater than 1000
    somelist.index(True, key=lambda x: sum(x.sales() > 1000))


Obviously not all such key functions are that simple and you may need 
to write a helper function, but the same applies to filter.

Some of these examples are a little artificial, in that you might want 
*all* of the items that match, not just the first. In that case, a 
filter function is probably better. Or you could loop, adjusting the 
start index each time. But if you do want only the first matching value, 
or if you know that there is only one such value, then a key function is 
more obvious than calling next and filter.


-- 
Steven
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/IVPAOSLKVJTBU367CMA6W2YT3BT7JQ5D/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to