Function suggestion: itertools.one()
Hi, There's a simple function that I use many times, and I think may be a good fit to be added to itertools. A function that gets an iterator, and if it has exactly one element returns it, and otherwise raises an exception. This is very useful for cases where I do some sort of query that I expect to get exactly one result, and I want an exception to be raised if I'm wrong. For example: jack = one(p for p in people if p.id == '1234') sqlalchemy already has such a function for queries: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query.... This is my implementation: def one(iterable): it = iter(iterable) try: r = next(it) except StopIteration: raise ValueError("Iterator is empty") try: next(it) except StopIteration: return r else: raise ValueError("Iterator has more than one item") What do you think? Thanks, Noam
I second this as being useful. However the “pythonic” way (whatever that means nowadays) is to do a for break else loop, which I think is kinda difficult to read as you need to make a few assumptions. Rollo
On 27 Jul 2020, at 20:06, Noam Yorav-Raphael <noamraph@gmail.com> wrote:
Hi,
There's a simple function that I use many times, and I think may be a good fit to be added to itertools. A function that gets an iterator, and if it has exactly one element returns it, and otherwise raises an exception. This is very useful for cases where I do some sort of query that I expect to get exactly one result, and I want an exception to be raised if I'm wrong. For example:
jack = one(p for p in people if p.id == '1234')
sqlalchemy already has such a function for queries: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query....
This is my implementation:
def one(iterable): it = iter(iterable) try: r = next(it) except StopIteration: raise ValueError("Iterator is empty") try: next(it) except StopIteration: return r else: raise ValueError("Iterator has more than one item")
What do you think?
Thanks, Noam _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/D52MPKLI... Code of Conduct: http://python.org/psf/codeofconduct/
I would think that you can use tuple unpacking for this? single_element, = some_iterator Will attempt to unpack the some_iterator into single_element and fail if single_element doesn't have exactly 1 element. It also has the added benefit that it works for any number of elements: one, two = some_iterator Example:
a = [1] one, = a one 1 b = [1, 2] one, = b
ValueError Traceback (most recent call last) <ipython-input-5-9b70ee0300f5> in <module> ----> 1 one, = b ValueError: too many values to unpack (expected 1)
c = [] one, = c
ValueError Traceback (most recent call last) <ipython-input-7-413c1b218942> in <module> ----> 1 one, = c ValueError: not enough values to unpack (expected 1, got 0) Best, Paul On 7/27/20 3:19 PM, Rollo Konig-Brock wrote:
I second this as being useful.
However the “pythonic” way (whatever that means nowadays) is to do a for break else loop, which I think is kinda difficult to read as you need to make a few assumptions.
Rollo
On 27 Jul 2020, at 20:06, Noam Yorav-Raphael <noamraph@gmail.com> wrote:
Hi,
There's a simple function that I use many times, and I think may be a good fit to be added to itertools. A function that gets an iterator, and if it has exactly one element returns it, and otherwise raises an exception. This is very useful for cases where I do some sort of query that I expect to get exactly one result, and I want an exception to be raised if I'm wrong. For example:
jack = one(p for p in people if p.id <http://p.id> == '1234')
sqlalchemy already has such a function for queries: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query....
This is my implementation:
def one(iterable): it = iter(iterable) try: r = next(it) except StopIteration: raise ValueError("Iterator is empty") try: next(it) except StopIteration: return r else: raise ValueError("Iterator has more than one item")
What do you think?
Thanks, Noam _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/D52MPKLI... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/3NA4E3QC... Code of Conduct: http://python.org/psf/codeofconduct/
A single-name unpacking assignment can do exactly what you want, albeit with slightly less helpful exception messages: jack, = (p for p in people if p.id == '1234') # note comma after the name jack If no value is yielded by the generator expression, you'll get "ValueError: not enough values to unpack (expected 1, got 0)". If multiple values are yielded, you'll instead get "ValueError: too many values to unpack (expected 1)".
On Tue, Jul 28, 2020 at 5:24 AM Steven Barker <blckknght@gmail.com> wrote:
A single-name unpacking assignment can do exactly what you want, albeit with slightly less helpful exception messages:
jack, = (p for p in people if p.id == '1234') # note comma after the name jack
Agreed. As a minor readability refinement, I would prefer to spell this with square brackets: [jack] = (p for p in people if p.id == '1234') The effect is identical, but for the one-item unpack, I prefer not to have the vital-but-subtle trailing comma. (You can use square brackets on larger assignment lists too, but they don't add anything.) ChrisA
Thanks for the suggestion! I agree that using a list is clearer that having a trailing comma, I like it! I still think that having a one() function would be useful, since: 1. I think it spells the intention more clearly. Also the exception would be easier to understand, since errors in tuple unpacking usually mean something else. 2. The one() function allows you to use the result inside an expression without assigning it to a variable. Cheers, Noam On Mon, Jul 27, 2020 at 10:30 PM Chris Angelico <rosuav@gmail.com> wrote:
On Tue, Jul 28, 2020 at 5:24 AM Steven Barker <blckknght@gmail.com> wrote:
A single-name unpacking assignment can do exactly what you want, albeit
with slightly less helpful exception messages:
jack, = (p for p in people if p.id == '1234') # note comma after
the name jack
Agreed. As a minor readability refinement, I would prefer to spell this with square brackets:
[jack] = (p for p in people if p.id == '1234')
The effect is identical, but for the one-item unpack, I prefer not to have the vital-but-subtle trailing comma.
(You can use square brackets on larger assignment lists too, but they don't add anything.)
ChrisA _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/62ZAKSHT... Code of Conduct: http://python.org/psf/codeofconduct/
I don't like consuming the iterator in the exception case. You might expect just one, but have a fallback approach for more. You could build the safer behavior using itertools.tee() or itertools.chain(). On Mon, Jul 27, 2020, 3:10 PM Noam Yorav-Raphael <noamraph@gmail.com> wrote:
Hi,
There's a simple function that I use many times, and I think may be a good fit to be added to itertools. A function that gets an iterator, and if it has exactly one element returns it, and otherwise raises an exception. This is very useful for cases where I do some sort of query that I expect to get exactly one result, and I want an exception to be raised if I'm wrong. For example:
jack = one(p for p in people if p.id == '1234')
sqlalchemy already has such a function for queries: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query....
This is my implementation:
def one(iterable): it = iter(iterable) try: r = next(it) except StopIteration: raise ValueError("Iterator is empty") try: next(it) except StopIteration: return r else: raise ValueError("Iterator has more than one item")
What do you think?
Thanks, Noam _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/D52MPKLI... Code of Conduct: http://python.org/psf/codeofconduct/
Shouldn’t this go to itertools-ideas? :-) On Mon, Jul 27, 2020 at 13:56 David Mertz <mertz@gnosis.cx> wrote:
I don't like consuming the iterator in the exception case. You might expect just one, but have a fallback approach for more. You could build the safer behavior using itertools.tee() or itertools.chain().
On Mon, Jul 27, 2020, 3:10 PM Noam Yorav-Raphael <noamraph@gmail.com> wrote:
Hi,
There's a simple function that I use many times, and I think may be a good fit to be added to itertools. A function that gets an iterator, and if it has exactly one element returns it, and otherwise raises an exception. This is very useful for cases where I do some sort of query that I expect to get exactly one result, and I want an exception to be raised if I'm wrong. For example:
jack = one(p for p in people if p.id == '1234')
sqlalchemy already has such a function for queries: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query....
This is my implementation:
def one(iterable): it = iter(iterable) try: r = next(it) except StopIteration: raise ValueError("Iterator is empty") try: next(it) except StopIteration: return r else: raise ValueError("Iterator has more than one item")
What do you think?
Thanks, Noam _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/D52MPKLI... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/N75BG5XV... Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido (mobile)
I'm sorry, I don't think I got that. I don't find a mailing list called itertools-ideas. Do you mean this should go to python-ideas? If so, I'm sorry for bothering python-dev, and I'll move this to python-ideas. I just read "The python-ideas list is for discussing more speculative design ideas" (https://www.python.org/community/lists/) as meaning discussing ideas for significant design changes, and not for adding a small function. Thanks, Noam
For ideas like this it is best to discuss them on python-ideas. I'll also mention that this idea has been brought up at least twice before: search for threads about itertools and single() or first() (if I remember correctly). On Mon, Jul 27, 2020 at 12:07 PM Noam Yorav-Raphael <noamraph@gmail.com> wrote:
Hi,
There's a simple function that I use many times, and I think may be a good fit to be added to itertools. A function that gets an iterator, and if it has exactly one element returns it, and otherwise raises an exception. This is very useful for cases where I do some sort of query that I expect to get exactly one result, and I want an exception to be raised if I'm wrong. For example:
jack = one(p for p in people if p.id == '1234')
sqlalchemy already has such a function for queries: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query....
This is my implementation:
def one(iterable): it = iter(iterable) try: r = next(it) except StopIteration: raise ValueError("Iterator is empty") try: next(it) except StopIteration: return r else: raise ValueError("Iterator has more than one item")
What do you think?
Thanks, Noam _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/D52MPKLI... Code of Conduct: http://python.org/psf/codeofconduct/
On Tue, 28 Jul 2020 at 15:16, Brett Cannon <brett@python.org> wrote:
For ideas like this it is best to discuss them on python-ideas.
I'll also mention that this idea has been brought up at least twice before: search for threads about itertools and single() or first() (if I remember correctly).
I think "first" even passed "general approval' last time around (about 1 month ago) - but I can't recall if someone took the responsibility of carrying that forward.
On Mon, Jul 27, 2020 at 12:07 PM Noam Yorav-Raphael <noamraph@gmail.com> wrote:
Hi,
There's a simple function that I use many times, and I think may be a good fit to be added to itertools. A function that gets an iterator, and if it has exactly one element returns it, and otherwise raises an exception. This is very useful for cases where I do some sort of query that I expect to get exactly one result, and I want an exception to be raised if I'm wrong. For example:
jack = one(p for p in people if p.id == '1234')
sqlalchemy already has such a function for queries: https://docs.sqlalchemy.org/en/13/orm/query.html#sqlalchemy.orm.query.Query....
This is my implementation:
def one(iterable): it = iter(iterable) try: r = next(it) except StopIteration: raise ValueError("Iterator is empty") try: next(it) except StopIteration: return r else: raise ValueError("Iterator has more than one item")
What do you think?
Thanks, Noam _______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/D52MPKLI... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-Dev mailing list -- python-dev@python.org To unsubscribe send an email to python-dev-leave@python.org https://mail.python.org/mailman3/lists/python-dev.python.org/ Message archived at https://mail.python.org/archives/list/python-dev@python.org/message/RO5PFRG4... Code of Conduct: http://python.org/psf/codeofconduct/
Thanks! I opened a new thread in python-ideas, here: https://mail.python.org/archives/list/python-ideas@python.org/thread/6OLEL4X... The "first" thread was very long, and was focused on a different function, first(). Perhaps a new thread, focused on one simple function in itertools, can reach a conclusion more easily. Thanks, Noam
participants (9)
-
Brett Cannon
-
Chris Angelico
-
David Mertz
-
Guido van Rossum
-
Joao S. O. Bueno
-
Noam Yorav-Raphael
-
Paul Ganssle
-
Rollo Konig-Brock
-
Steven Barker