54

In Python 3 I can do the following (see also PEP3132 on Extended Iterable Unpacking):

a, *b = (1, 2, 3)
# a = 1; b = (2, 3)

What can I do to achieve the same similarly elegant in Python 2.x?


I know that I could use single element access and slicing operations, but I wonder if there is a more pythonic way. My code so far:

a, b = (1, 2, 3)[0], (1, 2, 3)[1:]
# a = 1; b = (2, 3)
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
  • 3
    Seems like the explicit slicing is it, or using mutiple anonymous _ underscore vars to capture unwanted values: x, _, _ = tup – jdi Apr 24 '12 at 14:14
  • I actually have a question about this feature. Does it comply with the zen of python's "Explicit is better than implicit."? – jdi Apr 24 '12 at 14:52
  • 1
    @jdi it explicitly states: get me the first item to `a` and all other items to `b`. I find this very clear ... – moooeeeep Apr 24 '12 at 14:55
  • 1
    this is probably duplicate: http://stackoverflow.com/q/5333680/1025391 – moooeeeep Apr 24 '12 at 15:10

5 Answers5

33

I found out that the related PEP3132 gives some examples for Python 2.x as well:

Many algorithms require splitting a sequence in a "first, rest" pair:

first, rest = seq[0], seq[1:]

[...]

Also, if the right-hand value is not a list, but an iterable, it has to be converted to a list before being able to do slicing; to avoid creating this temporary list, one has to resort to

it = iter(seq)
first = it.next()
rest = list(it)

Other approaches given in the answers to this question:

Function Argument List Unpacking Approach

requires an extra function definition/call:

def unpack(first, *rest): 
  return first, rest
first, rest = unpack( *seq )

I wonder why it is implemented in unpacking function argument lists but not for normal tuple unpacking.

Generator Approach

Credits. Also requires a custom function implementation. Is a little more flexible concerning the number of first variables.

def unpack_nfirst(seq, nfirst):
  it = iter(seq)
  for x in xrange(nfirst):
    yield next(it, None)
  yield tuple(it)
first, rest = unpack_nfirst(seq, 1)

The most pythonic would probably be the ones mentioned in the PEP above, I guess?

Community
  • 1
  • 1
moooeeeep
  • 31,622
  • 22
  • 98
  • 187
  • 3
    As of today PEP 3132 only appears to apply to Python 3.x. I can confirm that the 'first, *rest = seq' syntax does not work in Python 2.7.5 (you mention 2.x above). – A. R. Younce Nov 30 '13 at 03:54
  • @Dave Thanks for bringing this up again. I decided to remove the section from the quoted part, because it was a source of confusion and just not relevant to the answer. – moooeeeep Apr 09 '15 at 17:31
23

I may be wrong but as far as I know

a, *b = (1, 2, 3)

is just syntactic sugar for slicing and indexing tuples. I find it useful but not very explicit.

Félix Cantournet
  • 1,941
  • 13
  • 17
  • 1
    To be perfectly honest, I'm still on py2.7 and haven't seen this 3.x addition yet. And it doesn't even look that much more useful than a slice. You obviously know you want the first element. Much prefer the slicing to the sugar. – jdi Apr 24 '12 at 14:22
  • while it may be just sugar, it becomes more important if your list on the left is larger than 2 (well, maybe larger than 6 or 7). The problem with slices is that you have to do some manual counting on both sides of the `=` which potentially makes the code harder to maintain. – Bryan Oakley Apr 24 '12 at 14:25
  • I have a list of lists, each of which of length 4 or more, and I wish I could do this in python2.7: `for a, b, c, d, *unused in myList: `. With appropriate names for a, b, c, d, I find it more expressive than having to index a single variable. – mcmlxxxvi Oct 23 '15 at 15:36
  • 1
    @mcmlxxxvi indeed, naming is (hard) and better. I recant my statement. – Félix Cantournet Feb 26 '16 at 10:47
8

I've got this handy little function:

def just(n, seq):
    it = iter(seq)
    for _ in range(n - 1):
        yield next(it, None)
    yield tuple(it)

For example:

a, b, c = just(3, range(5))
print a, b, c
## 0 1 (2, 3, 4)

also works with less arguments:

a, b, c = just(3, ['X', 'Y'])
print a, b, c
## X Y ()

In response to the comment, you can also define:

def take2(a, *rest): return a, rest
def take3(a, b, *rest): return a, b, rest
def take4(a, b, c, *rest): return a, b, rest
... etc

and use it like this:

p = (1,2,3)
a, b = take2(*p)
print a, b
## 1 (2, 3)
georg
  • 211,518
  • 52
  • 313
  • 390
  • 1
    That brings me to the idea: `def foo(x, *y): return x, y` and it would be `a, b = foo( *(1,2,3) )` ... – moooeeeep Apr 24 '12 at 14:41
  • 1
    @moooeeeep Good idea but you can simply do `a,b = foo(1,2,3)` instead of `a, b = foo( *(1,2,3) )` – jamylak Apr 24 '12 at 14:46
  • in production code in which file do you keep this handy little function? same file? different file? import what? do you package it and its friends? so in overall, how does it look in real code when you access `just`? – n611x007 Dec 12 '14 at 15:17
  • @naxa: for example `import myTools .... a,b = myTools.just(...)` – georg Dec 12 '14 at 15:28
4

I don't think there is any better way than the one you posted but here is an alternative using iter

>>> x = (1,2,3)
>>> i = iter(x)
>>> a,b = next(i), tuple(i)
>>> a
1
>>> b
(2, 3)
jamylak
  • 128,818
  • 30
  • 231
  • 230
2

Not sure about the context, but what about .pop(0)?

I see that there are tuples in your example, but if you want to do the sort of stuff you do, lists would be more suited, I think? (Unless there is some good reason for them to be immutable not given in the question.)

b = [1,2,3]
a = b.pop(0)
Bittrance
  • 2,202
  • 2
  • 20
  • 29