[Python-ideas] Generator unpacking

Andrew Barnert abarnert at yahoo.com
Fri Feb 12 19:03:31 EST 2016


On Feb 12, 2016, at 15:35, Michael Selik <mike at selik.org> wrote:
> 
>> On Fri, Feb 12, 2016 at 6:01 PM Erik <python at lucidity.plus.com> wrote:
>> On 12/02/16 19:54, Andrew Barnert via Python-ideas wrote:
>> > Meanwhile, you can always write the expanded version out explicitly. (And you can leave off the first line when you know c is already an iterator.) Or you can use itertools.islice to make it more compact:
>> >
>> >      >>> a, b = itertools.islice(c, 2)
>> >      >>> rest = c
>> 
>> Why not just have an itertools.unpack() - a simple version without
>> argument checking:
>> 
>> def unpack(seq, num):
>>    it = iter(seq)
>>    yield from (i[1] for i in zip(range(num), it))
>>    yield it

Creating a range just to zip with just to throw away the values seems like overcomplicating things. Unless there's some performance benefit to doing it that way, why not just keep it simple?

    def unpack(seq, num):
        it = iter(seq)
        yield from islice(it, num)
        yield it

Or, to be super novice-friendly:

    def unpack(seq, num):
        it = iter(seq)
        for _ in range(num): yield next(it)
        yield it

>> foo, bar, rest = unpack([1, 2, 3, 4, 5, 6], 2)

That doesn't really distinguish the "rest" very clearly.

Of course we could just change the last line to "yield [it]", and then call it with "foo, bar, *rest =", but that seems cheesy to me.

I don't know; if there is something better than islice to be found here, I think you're probably on the right track, but I don't think you're there yet, and I'm not sure there is anything to find. Unpacking syntax just feels "sequency" to me, and in a language with distinct sequences and iterators (instead of, say, lazy sequences like Haskell, or views wrapping many iterators like Swift), I think that means non-lazy. But hopefully I'm wrong. :)

>> Because it's in itertools, the expectation is that it has something to
>> do with iterators so the final return value always being an iterator
>> regardless of the original sequence type is reasonable (and is perhaps
>> the only justification for putting it in itertools in the first place ;) ).
> 
> There's some visual dissonance since the ``num`` argument is asking for the number of elements to unpack, but the left-hand of the assignment has num+1 variables.
> 
> What do you think about just using islice?
> 
>     >>> from itertools import islice
>     >>> it = iter(range(5))
>     >>> (first, second), rest = islice(it, 0, 2), it
>     >>> first
>     0
>     >>> second
>     1
>     >>> rest
>     <range_iterator object at 0x1011944b0>
> 
> I suppose it might read better broken apart into two lines to emphasize that the state changed.
> 
>     >>> first, second = islice(it, 0, 2)
>     >>> rest = it

That's exactly what was in my email, as the way to do things today, which he was replying to:

>> Or you can use itertools.islice to make it more compact:
>> >
>> >      >>> a, b = itertools.islice(c, 2)
>> >      >>> rest = c

So I think we can assume that he thinks his version improves over that, or he wouldn't have suggested it...
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20160212/8c219b69/attachment.html>


More information about the Python-ideas mailing list