>>> 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).
> 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.
that's wicked
i'd go for that
i'd definitely go for the a, b, *c = lst syntax though
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
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!
>
> 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.
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
Is it planned to be added to a close future Python 2.x version? I
really hope the answer is positive.
Bye,
bearophile
>> 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
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