New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add 'key' argument to list.index()
#113773
Comments
+1 |
I'm neutral on this. |
The example is not very motivating since the answer is |
It was not supposed to be motivating but illustrate how to find an object without exact equivalence. The original motivation is in the referenced PR where a search is performed for a dataclass instance based on an attribute, in the functional style. Since adding this to the c implementation is trivial, it seems preferable compared to a convoluted approach using 'indexOf' and 'map' It is equivalent to |
Changing the interface of basic types like list and tuple is problematic especially when that interface is specified in an ABC: Other types that are designed to mimic list or to satisfy the Sequence ABC would not be compatible with list any more if list changed the signature of its index method. There already is an In [9]: from operator import indexOf
In [10]: data = [(i, chr(i)) for i in range(100)]
In [11]: indexOf(map(lambda c: c[1] == 'a', data), True)
Out[11]: 97 Using If this is a common enough operation that using In [12]: def index_key(iterable, key):
...: """Return first item x in iterable such that key(x) is True"""
...: return indexOf(map(key, iterable), True)
...:
In [14]: index_key(data, lambda c: c[1] == 'a')
Out[14]: 97 If it is not worth adding a function for this that could work for any iterable then it is definitely not worth changing the interface of basic concrete types like list or the Sequence ABC. |
Sure, we could modify But the motivation here is specifically in the context of heaps which are based on concrete lists and where performance is important. The use of Regarding ABCs: Anyway, this is convenience/performance feature only, such as many features are, and maybe should be considered only in the context of #112498. |
Then it needs a performance justification: In [12]: N = 1000000
In [13]: a = list(range(N))
In [14]: %time a.index(N-1)
CPU times: user 44.9 ms, sys: 2 µs, total: 44.9 ms
Wall time: 44.3 ms
Out[14]: 999999
In [15]: from operator import indexOf
In [16]: %time indexOf(a, N-1)
CPU times: user 47.2 ms, sys: 0 ns, total: 47.2 ms
Wall time: 47 ms
Out[16]: 999999
In [17]: %timeit a.index(N-1)
17 ms ± 123 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [18]: %timeit indexOf(a, N-1)
22.2 ms ± 323 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) There is not much speed difference between In [20]: %time indexOf(map(abs, a), N-1)
CPU times: user 85.2 ms, sys: 0 ns, total: 85.2 ms
Wall time: 85 ms
Out[20]: 999999
In [22]: %time indexOf(map(lambda a: a if a >= 0 else -a, a), N-1)
CPU times: user 132 ms, sys: 1 µs, total: 132 ms
Wall time: 132 ms
Out[22]: 999999
In [24]: %timeit indexOf(map(abs, a), N-1)
46.4 ms ± 287 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [25]: %timeit indexOf(map(lambda a: a if a >= 0 else -a, a), N-1)
109 ms ± 6.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each) A list method is significantly less general than using In the first instance it would be better to see if |
Fair enough. I didn't realize that Is everyone happy with |
Also, I should note, |
It does, just not in the section specific to lists but in "Common Sequence Operations" of Sequence Types — list, tuple, range. Which the lists section also references: "Lists implement all of the common and mutable sequence operations. Lists also provide the following additional method:" |
One more thing: can you please also provide some prototype of the type-hint that this new parameter should have? This would be very helpful for us in Right now this seems rather hard in my opinion :( |
I see. It also mentions that "start" and "stop" arguments are not supported by all implementations. I only saw it for list, maybe it exists for tuple too. But anyway, it seems that modifying or adding an argument like this to a single implementation goes a bit against the grain. I guess enhancing A user can simply define def index_of_key(seq, val, *, key=None):
return indexOf(seq if key is None else map(key, seq), val) I guess we can drop this idea, then. Still, good talk. |
Type annotations is only my third language, but it is something like: T = TypeVar("T")
V = TypeVar("V")
class List[T];
@overload
def index(value: T, start: int=0, stop: int=maxsize, /) -> int:
...
@overload
def index(value: V, start: int=0, stop: int=maxsize, /, *, key: Callable[[T], V]) -> int:
...
def index(value: Any, start: int=0, stop: int=maxsize, /, *, key: Optional[Callable[[T], Any]]) -> int:
pass Yup, messy. The type of the first argument changes depending on the presence of a key argument. Again, does not speak in the favour of this proposal :( |
Actually, I guess the above is incorrect. In the end, a comparison is performed, PyObject_RichCompareBool(item, value), which can invoke the comparison operators on the list values. T = TypeVar("T")
class List[T];
def index(value: Any, start: int=0, stop: int=maxsize, /, *, key: Optional[Callable[[T], Any]]) -> int:
... It is interesting to note that the left side in the comparson is "item" and not "value" which makes it hard to create special comparison object to search for. the magic methods ( |
I'd be happy with adding Note that
That should be a separate change.
|
I've been persuaded that changing the list.index() method is not a good idea. I guess I should close this. adding key to indexOf might be much simpler. I guess I should close this? |
Yes, I guess it's best to close this issue and open a new one; reusing it for indexOf could be confusing. |
Feature or enhancement
Proposal:
It would be useful to be able to search a list in a functional-style manner. :
This idea came up in the discussion to pr #112498, in the context of searching for objects in a heap.
An implementation is provided in pr #113772
Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
I mentioned this in pr #112498, as a way to decouple searching and heap removal.
Instead of having
the desired functionality could be easily provided if
list
itself provided the search functionality:Linked PRs
The text was updated successfully, but these errors were encountered: