[Python-Dev] Extending tuple unpacking

Steve Holden steve at holdenweb.com
Tue Oct 11 18:22:06 CEST 2005


Nick Coghlan wrote:
> Greg Ewing wrote:
> 
>>Guido van Rossum wrote:
>>
>>
>>
>>>BTW, what should
>>>
>>>   [a, b, *rest] = (1, 2, 3, 4, 5)
>>>
>>>do? Should it set rest to (3, 4, 5) or to [3, 4, 5]?
>>
>>
>>Whatever type is chosen, it should be the same type, always.
>>The rhs could be any iterable, not just a tuple or a list.
>>Making a special case of preserving one or two types doesn't
>>seem worth it to me.
> 
> 
> And, for consistency with functions, the type chosen should be a tuple.
> 
> I'm also trying to figure out why you would ever write:
>    [a, b, c, d] = seq
> 
> instead of:
>    a, b, c, d = seq
> 
> or:
>    (a, b, c, d) = seq
> 
[...]
> So my vote would actually go for deprecating the use of square brackets to 
> surround an assignment target list - it makes it look like an actual list 
> object should be involved somewhere, but there isn't one.
> 
But don't forget that at present unpacking can be used at several levels:

 >>> ((a, b), c) = ((1, 2), 3)
 >>> a, b, c
(1, 2, 3)
 >>>

So presumably you'd need to be able to say

   ((a, *b), c, *d) = ((1, 2, 3), 4, 5, 6)

and see

   a, b, c, d == 1, (2, 3), 4, (5, 6)

if we are to retain today's multi-level consistency. And are you also 
proposing to allow

   a, *b = [1]

to put the empty list into b, or is that an unpacking error?


> 
>>>? And then perhaps
>>>
>>>   *rest = x
>>>
>>>should mean
>>>
>>>   rest = tuple(x)
>>>
>>>Or should that be disallowed
>>
>>Why bother? What harm would result from the ability to write that?
> 
> 
> Given that:
>    def foo(*args):
>        print args
> 
> is legal, I would have no problem with "*rest = x" being legal.
> 
Though presumably we'd still be raising TypeError is x weren't a sequence.
> 
>>>There certainly is a need for doing the same from the end:
>>>
>>>   *rest, a, b = (1, 2, 3, 4, 5)
>>
>>
>>I wouldn't mind at all if *rest were only allowed at the end.
>>There's a pragmatic reason for that if nothing else: the rhs
>>can be any iterable, and there's no easy way of getting "all
>>but the last n" items from a general iterable.
> 
> 
> Agreed. The goal here is to make the name binding rules consistent between for 
> loops, tuple assigment and function entry, not to create different rules.
> 
> 
>>>Where does it stop?
>>
>>For me, it stops with *rest only allowed at the end, and
>>always yielding a predictable type (which could be either tuple
>>or list, I don't care).
> 
> 
> For me, it stops when the rules for positional name binding are more 
> consistent across operations that bind names (although complete consistency 
> isn't possible, given that function calls don't unpack sequences automatically).
> 
Hmm. Given that today we can write

 >>> def foo((a, b), c):
...   print a, b, c
...
 >>> foo((1, 2, 3))
Traceback (most recent call last):
   File "<stdin>", line 1, in ?
TypeError: foo() takes exactly 2 arguments (1 given)
 >>> foo((1, 2), 3)
1 2 3
 >>>

does this mean that you'd also like to be able to write

   def foo((a, *b), *c):
     print a, b, c

and then call it like

   foo((1, 2, 3, 4), 5, 6)

to see

   1, (2, 3, 4), (5, 6)

[...]
> 
>>>BTW, and quite unrelated, I've always felt uncomfortable that you have to write
>>>
>>>   f(a, b, foo=1, bar=2, *args, **kwds)
>>>
>>>I've always wanted to write that as
>>>
>>>   f(a, b, *args, foo=1, bar=2, **kwds)
>>
>>
>>Yes, I'd like that too, with the additional meaning that
>>foo and bar can only be specified by keyword, not by
>>position.
> 
> 
> Indeed. It's a (minor) pain that optional flag variables and variable length 
> argument lists are currently mutually exclusive. Although, if you had that 
> rule, I'd want to be able to write:
> 
>    def f(a, b, *, foo=1, bar=2): pass
> 
> to get a function which required exactly two positional arguments, but had a 
> couple of optional keyword arguments, rather than having to do:
> 
>    def f(a, b, *args, foo=1, bar=2):
>      if args:
>        raise TypeError("f() takes exactly 2 positional arguments (%d given)",
>                         2 + len(args))
> 
I do feel that for Python 3 it might be better to make a clean 
separation between keywords and positionals: in other words, of the 
function definition specifies a keyword argument then a keyword must be 
used to present it.

This would allow users to provide an arbitrary number of positionals 
rather than having them become keyword arguments. At present it's 
difficult to specify that.

regards
  Steve
-- 
Steve Holden       +44 150 684 7255  +1 800 494 3119
Holden Web LLC                     www.holdenweb.com
PyCon TX 2006                  www.python.org/pycon/



More information about the Python-Dev mailing list