Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

* for generic unpacking and not just for arguments?

1 view
Skip to first unread message

Russell Warren

unread,
Nov 29, 2009, 9:25:24 AM11/29/09
to
Is there a reason that this is fine:

>>> def f(a,b,c):
... return a+b+c
...
>>> f(1, *(2,3))
6

but the code below is not?

>>> x = (3, 4)
>>> (1, 2, *x) == (1, 2, 3, 4)
Traceback (most recent call last):
File "<string>", line 1, in <fragment>
invalid syntax: <string>, line 1, pos 8

Why does it only work when unpacking arguments for a function? Is it
because the code below is preferred, and more readable?

>>> x = (3, 4)
>>> (1, 2) + x == (1, 2, 3, 4)
True

I've rooted around to see if there is an answer already and found some
threads going way back to 1998 (!!), but can't find a concise answer
as to why it is limited to args.

I don't have a burning desire for this to work, but I just tried it
unsuccessfully when building up a tuple and was mildly surprised that
it didn't work, so I'm curious why.

Maybe it's just that * is strictly for arguments, and trying it for
generic tuple unpacking is abuse (which is down the corridor in 12A).

Mel

unread,
Nov 29, 2009, 10:40:29 AM11/29/09
to
Russell Warren wrote:

> Maybe it's just that * is strictly for arguments, and trying it for
> generic tuple unpacking is abuse (which is down the corridor in 12A).

I'd agree with that. It's a feature of function calls, not a feature of
sequence types, so that you can handle a set of function arguments as a
bunch, and apply them when you want.

The other function call feature that sequence types don't do is

a, b, c = **{'b':3, 'c':a, 'a':c}


Mel.


inhahe

unread,
Nov 29, 2009, 11:09:04 AM11/29/09
to mwi...@the-wire.com, pytho...@python.org

that's wicked
i'd go for that
i'd definitely go for the a, b, *c = lst syntax though

Christian Heimes

unread,
Nov 29, 2009, 11:09:09 AM11/29/09
to pytho...@python.org
Russell Warren wrote:
> but the code below is not?
>
>>>> x = (3, 4)
>>>> (1, 2, *x) == (1, 2, 3, 4)
> Traceback (most recent call last):
> File "<string>", line 1, in <fragment>
> invalid syntax: <string>, line 1, pos 8
>
> Why does it only work when unpacking arguments for a function? Is it
> because the code below is preferred, and more readable?
>
>>>> x = (3, 4)
>>>> (1, 2) + x == (1, 2, 3, 4)
> True
>
> I've rooted around to see if there is an answer already and found some
> threads going way back to 1998 (!!), but can't find a concise answer
> as to why it is limited to args.

The feature is available in Python 3.x:

>>> a, b, *c = 1, 2, 3, 4, 5
>>> a, b, c
(1, 2, [3, 4, 5])
>>> a, *b, c = 1, 2, 3, 4, 5
>>> a, b, c
(1, [2, 3, 4], 5)

Christian

Russell Warren

unread,
Nov 29, 2009, 1:01:42 PM11/29/09
to
On Nov 29, 11:09 am, Christian Heimes <li...@cheimes.de> wrote:
> The feature is available in Python 3.x:
>
> >>> a, b, *c = 1, 2, 3, 4, 5
> >>> a, b, c
> (1, 2, [3, 4, 5])
> >>> a, *b, c = 1, 2, 3, 4, 5
> >>> a, b, c
>
> (1, [2, 3, 4], 5)

Interesting... especially the recognition of how both ends work with
the "a, *b, c" example. That is some funky syntax. And it goes to a
list instead of a tuple now, I see.

It is also the opposite of what I was considering, although I expect
it goes both ways like it does for functions? The given python 3.0
example is on the LHS with * packing many-to-one while unpacking (like
*args inside a function), whereas I was referring to RHS-style
unpacking where * explodes/unpacks one-to-many (like passing *args
_to_ a function).

I've steered clear of 3.x so far, but it looks like I'll have to give
it a whirl if only to avoid asking irrelevant questions!

Lie Ryan

unread,
Nov 29, 2009, 2:21:44 PM11/29/09
to
On 11/30/2009 1:25 AM, Russell Warren wrote:

>
> Maybe it's just that * is strictly for arguments, and trying it for
> generic tuple unpacking is abuse (which is down the corridor in 12A).

Because (1, 2, *x) == (1, 2, 3, 4) is not tuple unpacking [!]


Tuple unpacking is related with assignment, e.g.:
a, b, c = 1, 2, 3

In python 3, there is the extended tuple unpacking:
>>> a, b, *c = [1, 2, 3, 4, 5]
>>> # a = 1; b = 2; c = [3, 4, 5]
>>> a, *b, c = [1, 2, 3, 4, 5]
>>> # a = 1; b = [2, 3, 4]; c = 5


If I remember correctly, the idea of (1, 2, *x) to mean (1, 2) + x has
been discussed before, and rejected as well.

Tim Chase

unread,
Nov 29, 2009, 2:45:18 PM11/29/09
to Christian Heimes, pytho...@python.org
> The feature is available in Python 3.x:
>
>>>> a, b, *c = 1, 2, 3, 4, 5
>>>> a, b, c
> (1, 2, [3, 4, 5])
>>>> a, *b, c = 1, 2, 3, 4, 5
>>>> a, b, c
> (1, [2, 3, 4], 5)

This is a nice feature of 3.x but I'm disappointed (especially in
light of the move to make more things iterators/generators), that
the first form unpacks and returns a list instead returning the
unexhausted generator. There are times I've wanted to write
something like

def powers_of_n(n=2):
p = 0
while True:
yield n ** p
p += 1

def linear(i=0, step=1):
while True:
yield i
i += step

CONST_A, CONST_B, CONST_C, *_ = linear()
OPT_A, OPT_B, OPT_C, *_ = powers_of_n()

because adding another CONST or OPT becomes trivial (just add it
to the list of names). I currently have to do something like

CONST_A, CONST_B, CONST_C = range(3)

and then remember to bump up my range() parameter when I create a
new value on the left. It's not bad to see here, but I often
have times where N>60 constants and tweaking something in an
alphabetically sorted list may require scrolling to see/remember
the range() change.


However, because 3.x tries to make a list out of it, this doesn't
help me at all because my generator is infinite. Boo.


Diez Roggisch gave me a beautiful (okay, the implementation code
is a bit ugly, but the final product's simplicity achieves
elegance) solution to my similar want for 2.x here:

http://www.mail-archive.com/pytho...@python.org/msg87022.html

-tkc


Bearophile

unread,
Nov 29, 2009, 4:08:40 PM11/29/09
to
Christian Heimes:

> The feature is available in Python 3.x:
>
> >>> a, b, *c = 1, 2, 3, 4, 5

Is it planned to be added to a close future Python 2.x version? I
really hope the answer is positive.

Bye,
bearophile

Steven D'Aprano

unread,
Nov 30, 2009, 9:50:39 AM11/30/09
to
On Sun, 29 Nov 2009 13:45:18 -0600, Tim Chase wrote:

>> The feature is available in Python 3.x:
>>
>>>>> a, b, *c = 1, 2, 3, 4, 5
>>>>> a, b, c
>> (1, 2, [3, 4, 5])
>>>>> a, *b, c = 1, 2, 3, 4, 5
>>>>> a, b, c
>> (1, [2, 3, 4], 5)
>
> This is a nice feature of 3.x but I'm disappointed (especially in light
> of the move to make more things iterators/generators), that the first
> form unpacks and returns a list instead returning the unexhausted
> generator.

So you want *x behave radically different in subtly different contexts?

a, *x, b = iterator

would give x a list and iterator run to exhaustion, whereas:

a, b, *x = iterator

would give x identical to iterator, which hasn't run to exhaustion, while
both of these:

a, *x, b = sequence
a, b, *x = sequence

would give x a list, but in the second case, unlike the case of
iterators, x would not be identical to sequence.


No thank you, I'd prefer a nice, consistent behaviour than a magical "Do
what I mean" function.

However, having some syntax giving the behaviour you want would be nice.
Something like this perhaps?

a, b, c = *iterable

equivalent to:

_t = iter(iterable)
a = next(_t)
b = next(_t)
c = next(_t) # and so on for each of the left-hand names
del _t

That would be useful.


--
Steven

Tim Chase

unread,
Nov 30, 2009, 11:58:48 AM11/30/09
to Steven D'Aprano, pytho...@python.org
> So you want *x behave radically different in subtly different contexts?
>
> a, *x, b = iterator
>
> would give x a list and iterator run to exhaustion, whereas:

Using the infix "*x" I'd be fine with x being an iterator too in
the same way as the postfix "*x" I'd enjoy, but this would
require consuming all-but-the-remaining-fixed and the converting
the middle bits back into an iterator...which isn't that much
different from the current implementation of "a, *x, b = itr" with

x = iter(x)

after the tuple assignment. Not a great gain, but at least
consistent to remove that burr.

It doesn't allow for infinite generators, but that's a somewhat
nonsensical request anyways :) "b = INF"? "b=-INF"? "b=K"?
and it gets weirder with

a,*x,b,c,d = inf_seq_of_monotonically_increasing_primes()

if you can get me b, c, and d in a finite amount of time, you get
some sorta prize :)

> However, having some syntax giving the behaviour you want would be nice.
> Something like this perhaps?
>
> a, b, c = *iterable
>
> equivalent to:
>
> _t = iter(iterable)
> a = next(_t)
> b = next(_t)
> c = next(_t) # and so on for each of the left-hand names
> del _t
>
> That would be useful.

But yes, that's even better for my use case, as it lacks the ugly
"*_" sponge to soak up the remainder and removes my need to track
the number of items on the LHS of the assignment. That seems it
would involve a new Python construct/syntax rather than the
existing py3k syntax. Or at least the removal of the
"ValueError: too many values to unpack" error. That would be a
lovely addition (when the new-feature-ban is lifted :)

-tim


0 new messages