Thanks! It's good to hear that you too find it useful.
Since adding methods to built-in types is much heavier than adding one
function to a module, l suggest keeping this discussion focused on adding
just the one() function to itertools, and see if there is enough support
for this.
Cheers,
Noam
On Tue, Jul 28, 2020 at 10:47 PM Alex Hall wrote:
> In my personal toolbox of utility functions, this is by far the function I
> use most often, although it's implemented slightly differently and I call
> it `only`. I think it's very useful and it would be great to have in the
> standard library to encourage people to write safer code.
>
> Often this is part of a larger expression, especially if I'm drilling into
> some nested data structure. This can lead to having a prefix operation (the
> function call) breaking a chain of postfix operations (attributes, method
> calls, subscripting...) which is ugly and less readable. It would be nice
> if this could also be available as a method on lists, tuples, and sets to
> keep the data flowing left to right. Plus it would save an import.
>
> On Tue, Jul 28, 2020 at 9:29 PM Noam Yorav-Raphael
> wrote:
>
>> Hello,
>>
>> 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.one
>>
>> more-itertools has this exact function:
>>
>> https://more-itertools.readthedocs.io/en/latest/api.html#more_itertools.one
>>
>> Here is a simple implementation:
>>
>> def one(iterable):
>> it = iter(iterable)
>> try:
>> first = next(it)
>> except StopIteration:
>> raise ValueError("Iterator is empty")
>> try:
>> second = next(it)
>> except StopIteration:
>> return first
>> else:
>> raise ValueError("Iterator has more than one item")
>>
>> I brought this up on python-dev, but it should be discussed here.
>> Here is the discussion:
>>
>> https://mail.python.org/archives/list/python-...@python.org/thread/D52MPKLIN4VEXBOCKVMTWAK66MAOEINY/
>>
>> Brett Cannon said that this idea has been brought up at least twice
>> before. I found:
>>
>> https://mail.python.org/archives/list/python-ideas@python.org/thread/FTJ6JRDTZ57HUVZ3PVIZV2NHU2NLAC4X/#RMWV3SNZ2N4KZLPKPIDE42H46QDEIVHE
>>
>>
>> https://mail.python.org/archives/list/python-ideas@python.org/thread/REYDJFCXQNQG4SAWKELQMCGM77IZG47Q/#ITR2ILPVCKYR52U2D7RHGENASZTNVDHN
>>
>> The first thread hasn't reached any operative conclusion. The second
>> thread was very long, and seemed to focus mostly on another function,
>> first(), that doesn't check if there is more than one item. Joao S. O Bueno
>> said that it passed "general approval". I think that perhaps a new
>> discussion, focused just on one (no pun intended) function in the itertools
>> module may reach a conclusion.
>>
>> It was suggested that instead of an additional function, one can use
>> iterator unpacking:
>>
>> jack, = (p for p in people if p.id == '1234')
>> or
>> [jack] = (p for p in people if p.id == '1234')
>>
>> I still think that having a one() function would be useful, since:
>> 1. I think it spells the intention more clearly. It is not symbols that
>> you need to understand their meaning in order to understand that I expect
>> the iterable to have exactly one item, it's spelled in code.
>> 2. The exception would be easier to understand, since errors in tuple
>> unpacking usually mean something else.
>> 3. The one() function allows you to use the result inside an expression
>> without assigning it to a variable. Therefore, I think it encourages
>> writing better code. It's very easy to write:
>> print([p for p in people if p.id == '1234][0])
>> (which has the problem of not verifying the assumption that there's no
>> more than one result), and I find it easier to replace _[0] with one(_)
>> than to be required to name a new variable, and instead of having an
>> operation on the iterable, c