3

I've got a function that takes input and returns a 3 item tuple:

def f(a, b):
    x = a + 1
    y = x + 2
    z = y + 3
    return (x, y, z)

And I start with a set of tuples:

my_set = {(1, 2), (3, 4), ... (m, n)}

And I need to build a list, but the follow throws a TypeError:

[(a, b, x, y, z, (x, y, z)) for a, b in my_set for x, y, z in f(a, b)]
TypeError: 'int' object is not iterable

I was reviewing this post which shows a similar process, but for some reason the for x, y, z ... is throwing the exception and I'm not sure if I am just overlooking something small or not.

pstatix
  • 3,611
  • 4
  • 18
  • 40

2 Answers2

5

It's valid to unpack f(a, b) into x, y, z, but that's not what you're doing. for x, y, z in f(a, b) attempts to unpack each element of f(a, b) as if each element was an individual 3-tuple.

What you're looking for is a way to assign f(a, b) to something in a list comprehension, without iterating over it. There are workarounds for that, but I'd recommend just using a normal loop:

l = []
for a, b in my_set:
    x, y, z = f(a, b)
    l.append((a, b, x, y, z, (x, y, z)))

If you really want to use a list comprehension, one workaround is to loop over a dummy list [f(a, b)] instead of f(a, b):

[(a, b, x, y, z, (x, y, z)) for a, b in my_set for x, y, z in [f(a, b)]]
user2357112
  • 260,549
  • 28
  • 431
  • 505
  • Eh, I was looking for a dupe, but can't hit on the right keywords. This'll do. – DSM Jun 27 '18 at 18:11
  • Ah. Good explanation. Yes in my mind I thought I could translate the `for x, y, z` to equate the `*` operator (unpack). But now I see that it is like trying to take each integer inside the tuple and it into 3 parts. – pstatix Jun 27 '18 at 18:16
  • If only we could `[(a, b, x, y, z, (x, y, z)) for a, b, in my_set for x, y, z in *f(a, b)]` – pstatix Jun 27 '18 at 18:20
  • That would take something like a (fictional) `where` keyword. `[(a, b, x, y, z, (x, y, z)) for a, b, in my_set where x, y, z = f(a, b)]`. This is interestingly a place where the equivalent in Haskell would be more succinct. `[let (x, y, z) = f a b in (a, b, x, y, z, (x, y, z)) | (a, b) <- my_set]` – Adam Smith Jun 27 '18 at 18:22
1

Let's understand your one-liner. Here's what you're actually doing:

for a, b in my_set:
    for i in f(a, b):
        print(i)

For every a & b in my_set function f is calculated. It outputs a tuple and then i is iterated over the tuple.

Because every item in the tuple is an integer,for x, y, z in f(a, b) doesn't work. Integer can't be split into several fractions.

You need:

[(a, b, *f(a, b), f(a, b)) for a, b in my_set]

In case efficiency matters you need for loop:

output = list()
for a, b in my_set:
    x, y, z = f(a, b)
    output.append((a, b, x, y, z, (x, y, z))
koPytok
  • 3,453
  • 1
  • 14
  • 29