[Python-Dev] Half-baked proposal: * (and **?) in assignments

Brett Cannon bac@OCF.Berkeley.EDU
Sat, 23 Nov 2002 18:00:29 -0800 (PST)


[Delaney, Timothy]

> > From: Brett Cannon [mailto:bac@OCF.Berkeley.EDU]
> >
> > But is it that big of a deal to just take the [1:] index of something
> > instead of using this suggested addition?
>
> Actually, it can be.
>
>     a = func()
>     b, c = a[:1], a[1:]
>
> vs
>
>     b, *c = func()
>
> The first version leads to namespace pollution, which you cannot safely
> ignore. The two options are:
>
>     a = func()
>     b, c = a[:1], a[1:]
>     del a
>
> in which case you may at times fail to del a (probably not a big issue if an
> exception has been thrown ... unless a is global, but we don't do that ;)
>

Which was the argument I used  with the time.localtime() discussion.
Negating each other's arguments is not helping.  =)

>     b = func()
>     b, c = b[:1], b[1:]
>
> which again may potentially screw up.
>
> Additionally, the
>
>     b, *c = func()
>
> version will work with any iterator on the RHS, unlike the slicing version.
> I see two options for this:
>
> 1. b is assigned the first value, c is assigned a tuple of the remaining
> values
>
> 2. b is assigned the first value, c is assigned the iterator.
>
> Hmm - on further thought, #2 may be the way to go - the value with a * is
> always assigned the result of calling iter() on the remainder.
>

#2 is the better option.  But it that complicated to write as a function::

	def peel(iterator, arg_cnt):
		if not hasattr(iterator, 'next'):
			iterator = iter(iterator)
		for num in xrange(arg_cnt):
			yield iterator.next()
		else:
			yield iterator

	>>> a,b,c = peel((1,2,3,4,5), 2)
	>>> a
	1
	>>> b
	2
	>>> c
	<tupleiterator object at 0x3c6a80>
	>>> c.next()
	3

Or you  could just do it the old-fashioned way and just write out the two
``.next()`` calls.  =)

> That would also produce a distinction between
>
>     a = t    # a is assigned a tuple
>     *a = t   # a is assigned a tuple iterator
>
> In that case,
>
>     a, *b, c = t
>
> would have to perform the following shennanigans:
>
> 1. Extract all the values.
> 2. Assigned the first value to a, and the last value to c.
> 3. Create a tuple out of the rest, get its iterator and assign that to b.
>
> In the common case however of:
>
>     a, b, *c = t
>
> that could be reduced to:
>
> 1. Get the iterator.
> 2. Assign the result of .next() to a.
> 3. Assign the result of .next() to b.
> 4. Assign the iterator to c.
>
> i.e. if the asterisk is attached to any but the last name of the LHS, there
> better not be an infinitly-long iterable in the RHS ;)
>

=)  No.  That would not be good.

I still don't love this whole idea.  But I will admit, though, that
viewing this more as a syntactically easy way to unrolling an iterator
makes it seem more reasonable.  Ditch the possibility of having the
arbitrary assignment variable anywhere but the end of the assignment and
you might be able to get me to a -0 vote with some good examples of where
someone could have really used this.

But if  you do it like this with iterators the whole initial suggestion of
making parameter passing just like assignment goes out the door.

-Brett