What Is the Pythonic Way to Unpack Tuples

What is the pythonic way to unpack tuples?

Generally, you can use the func(*tuple) syntax. You can even pass a part of the tuple, which seems like what you're trying to do here:

t = (2010, 10, 2, 11, 4, 0, 2, 41, 0)
dt = datetime.datetime(*t[0:7])

This is called unpacking a tuple, and can be used for other iterables (such as lists) too. Here's another example (from the Python tutorial):

>>> range(3, 6)             # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args) # call with arguments unpacked from a list
[3, 4, 5]

Unpack tuples inside a tuple

If you're looking to flatten a general tuple of tuples, you could:

  1. use a list/generator comprehension
flattened_tup = tuple(j for i in tup for j in i)

  1. use itertools
import itertools
flattened_tup = tuple(itertools.chain.from_iterable(tup))

Unpack python tuple with [ ]'s

No, those are all exactly equivalent. One way to look at this empirically is to use the dis dissasembler:

>>> import dis
>>> dis.dis("a, b, c = (1, 2, 3)")
1 0 LOAD_CONST 0 ((1, 2, 3))
2 UNPACK_SEQUENCE 3
4 STORE_NAME 0 (a)
6 STORE_NAME 1 (b)
8 STORE_NAME 2 (c)
10 LOAD_CONST 1 (None)
12 RETURN_VALUE
>>> dis.dis("(a, b, c) = (1, 2, 3)")
1 0 LOAD_CONST 0 ((1, 2, 3))
2 UNPACK_SEQUENCE 3
4 STORE_NAME 0 (a)
6 STORE_NAME 1 (b)
8 STORE_NAME 2 (c)
10 LOAD_CONST 1 (None)
12 RETURN_VALUE
>>> dis.dis("[a, b, c] = (1, 2, 3)")
1 0 LOAD_CONST 0 ((1, 2, 3))
2 UNPACK_SEQUENCE 3
4 STORE_NAME 0 (a)
6 STORE_NAME 1 (b)
8 STORE_NAME 2 (c)
10 LOAD_CONST 1 (None)
12 RETURN_VALUE
>>>

From the formal language specification, this is detailed here. This is part of the "target list", A relevant quote:

Assignment of an object to a target list, optionally enclosed in
parentheses or square brackets,
is recursively defined as
follows....

Python Tuple Unpacking

Use zip, then unpack:

nums_and_words = [(1, 'one'), (2, 'two'), (3, 'three')]
nums, words = zip(*nums_and_words)

Actually, this "unpacks" twice: First, when you pass the list of lists to zip with *, then when you distribute the result to the two variables.

You can think of zip(*list_of_lists) as 'transposing' the argument:

   zip(*[(1, 'one'), (2, 'two'), (3, 'three')])
== zip( (1, 'one'), (2, 'two'), (3, 'three') )
== [(1, 2, 3), ('one', 'two', 'three')]

Note that this will give you tuples; if you really need lists, you'd have to map the result:

nums, words = map(list, zip(*nums_and_words))

How to unpack single-item tuples in a list of lists

You need nested comprehensions (with either a two layer loop in the inner comprehension, or use chain.from_iterable for flattening). Example with two layer loop (avoids need for imports), see the linked question for other ways to flatten the inner list of tuples:

>>> listolists = [[('1st',), ('2nd',), ('5th',)], [('1st',)]]
>>> [[x for tup in lst for x in tup] for lst in listolists]
[['1st', '2nd', '5th'], ['1st']]

Note that in this specific case of single element tuples, you can avoid the more complicated flattening with just:

 >>> [[x for x, in lst] for lst in listolists]

per the safest way of getting the only element from a single-element sequence in Python.

Clarifications on Python tuple unpacking

The catch is that the brackets of a parameter list always enclose a tuple.
They are brackets you could not omit. So they are not mixed up with operator-priority-brackets

By the way, fun fact:
write (NOTE THE COMMA AFTER THE x)

*x, = (1,2,3,4,5)

Then it works, just like you would neet to add a comma in a bracket to make it a tuple.
like (1) is no tuple, but (1,) is

How to unpack a tuple of lists

You can use the indexing of your tuple and then the lists to access the inner-most elements. For example, to get at the string 'a', you could call:

myTuple[0][0]

If you wanted to iterate over all the elements in the lists, you could use the chain method form itertools. For example:

from itertools import chain

for i in chain(*myTuple):
print(i)

Unpacking list of tuples

Let's look at what you have:

testcases = [([1, 1, 1], 2, 2)]

This is a list. Of size one. So testcases[0] is the only element there is.

So this code:

for a, b, c in testcases:
pass

is a loop of length one. So each time through the loop (that is just the once), you get the element: ([1, 1, 1], 2, 2) which is a tuple. Of size three.

So unpacking that: a,b,c = testcases[0] gives:

a == [1, 1, 1]
b == 2
c == 2

which is what you see printed.

How to unpack a tuple when indexing?

The problem with:

a[:, :, np.triu_indices(14)]

is that you are using as argument for [] a tuple of mixed types slice and tuple (tuple(slice, slice, tuple(np.ndarray, np.ndarray))) and not a single tuple (eventually with advanced indexing), e.g. tuple(slice, slice, np.ndarray, np.ndarray).
This is causing your troubles. I would not go into the details of what is happening in your case.

Changing that line to:

a[(slice(None),) * 2 + np.triu_indices(14)]

will fix your issues:

a[(slice(None),) * 2 + np.triu_indices(14)].shape
# (100, 50, 105)

Note that there are a couple of ways for rewriting:

(slice(None),) * 2 + np.triu_indices(14)

another way may be:

(slice(None), slice(None), *np.triu_indices(14))

Also, if you want to use the ... syntax, you need to know that ... is syntactic sugar for Ellipsis, so that:

(Ellipsis,) + np.triu_indices(14)

or:

(Ellipsis, *np.triu_indices(14))

would work correctly:

a[(Ellipsis,) + np.triu_indices(14)].shape                                                   
# (100, 50, 105)
a[(Ellipsis, *np.triu_indices(14))].shape
# (100, 50, 105)

How are tuples unpacked in for loops?

You could google "tuple unpacking". This can be used in various places in Python. The simplest is in assignment:

>>> x = (1,2)
>>> a, b = x
>>> a
1
>>> b
2

In a for-loop it works similarly. If each element of the iterable is a tuple, then you can specify two variables, and each element in the loop will be unpacked to the two.

>>> x = [(1,2), (3,4), (5,6)]
>>> for item in x:
... print "A tuple", item
A tuple (1, 2)
A tuple (3, 4)
A tuple (5, 6)
>>> for a, b in x:
... print "First", a, "then", b
First 1 then 2
First 3 then 4
First 5 then 6

The enumerate function creates an iterable of tuples, so it can be used this way.



Related Topics



Leave a reply



Submit