The Wayback Machine - https://web.archive.org/web/20250616091405/https://github.com/python/cpython/pull/113772
Skip to content

gh-113773: add list.index() "key" named argument #113772

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

Closed
wants to merge 7 commits into from

Conversation

kristjanvalur
Copy link
Contributor

@kristjanvalur kristjanvalur commented Jan 6, 2024

This PR adds a keyword-only key argument to list.index().
When provided, each list item is transformed by the key callable before being compared to value, thus allowing
a functional-style search of an item in a list:

data = [(i, chr(i)) for i in range(100)]
index_of_a  = data.index("a", key=lambda i: i[1])

This is based on an idea by @rhettinger from the discussion in PR #112498, where a case for finding an object in a list via
keyed lookup is made. This PR applies that idea directly to the list object underlying the heap in question.

list.index() is not explicitly documented anywhere, so there is no doc update.

@kristjanvalur kristjanvalur changed the title gh-NNNNN: add list.index() "key" named argument gh-113773: add list.index() "key" named argument Jan 6, 2024
@kristjanvalur kristjanvalur marked this pull request as ready for review January 6, 2024 14:33
Comment on lines 2688 to 2689
if (!key)
for (i = start; i < stop && i < Py_SIZE(self); i++) {
Copy link
Contributor

@grigoriev-semyon grigoriev-semyon Jan 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can add
int cmp before for if stmt

and modify for-loops like that:
for (i = start, cmp = 0; i < stop && i < Py_SIZE(self) && cmp == 0; i++)

reference:

for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's pretty pointless, also not the way the old code was. anyway, I've now modified it.

else if (cmp < 0)
return NULL;
}
if (!key)
Copy link
Member

@Eclips4 Eclips4 Jan 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP 7

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was following the style of the cod,e but ok, pep 7 for new code.

Comment on lines 2711 to 2714
if (cmp > 0)
return PyLong_FromSsize_t(i);
else if (cmp < 0)
return NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (cmp > 0)
return PyLong_FromSsize_t(i);
else if (cmp < 0)
return NULL;
if (cmp < 0) {
return NULL;
}
return PyLong_FromSsize_t(i);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not eq when cmp == 0

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, you're right.
There's should be

if (cmp == -1) {
    return NULL;
}
else if (cmp == 1 ) {
    return PyLong_FromSsize_t(i);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the original code, only indented. See newest version.

Comment on lines 2695 to 2698
if (cmp > 0)
return PyLong_FromSsize_t(i);
else if (cmp < 0)
return NULL;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (cmp > 0)
return PyLong_FromSsize_t(i);
else if (cmp < 0)
return NULL;
if (cmp < 0) {
return NULL;
}
return PyLong_FromSsize_t(i);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asgain, original code, with indent.

else if (cmp < 0)
return NULL;
}
else
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP 7

Py_INCREF(item);
PyObject *obj = PyObject_CallFunctionObjArgs(key, item, NULL);
Py_DECREF(item);
if (!obj)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP 7

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be good if you pointed to the particular pep7 violation you have in mind. I don't think constructs such as !obj are forbidden, if that is what you mean.

@Eclips4
Copy link
Member

Eclips4 commented Jan 6, 2024

I do not know, would it be supported by others, but this kind of change definitely needs a NEWS entry and documentation update for list.index.

@kristjanvalur
Copy link
Contributor Author

I do not know, would it be supported by others, but this kind of change definitely needs a NEWS entry and documentation update for list.index.

NEWS added. list.index is not documented anywhere except in-line.

@@ -2659,38 +2659,56 @@ list.index
start: slice_index(accept={int}) = 0
stop: slice_index(accept={int}, c_default="PY_SSIZE_T_MAX") = sys.maxsize
/
*
key: object=NULL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AC has problems with NULL default. To check this please run inspect.signature(list.index) before and after.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you saying that object=NULL is nowhere used in the code despite being a documented convenience way of signifying optional PyObject values? So, either fix AC, or the docs?
I was an active contributor before AC was introduced and I'm only learning it now.
I guess you want me to turn this into object=None and make comparisons with PyNone in the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

del data[:]
return i[1]

with self.assertRaises(ValueError):
Copy link
Member

@sobolevn sobolevn Jan 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, assert the error message here. ValueError is too broad.

{
Py_ssize_t i;

if (start < 0) {
start += Py_SIZE(self);
if (start < 0)
if (start < 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen other comments, but I have an oposite opinion: diff should be as minimal as possible. This is just a comment, no action needed, unless others also agree :)

Copy link
Contributor Author

@kristjanvalur kristjanvalur Jan 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I respectfully disagree, the style should be consistent within the function. Either I convert the entire function to pep7, or stick to the existing style. Which should it be? Also, it is a bit hard to try to follow disagreeing opinions of reviewers.

@encukou
Copy link
Member

encukou commented Mar 20, 2024

Closing; see the issue for details. Adding key to operator.indexOf would be a better way.

@encukou encukou closed this Mar 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants