Skip to content
Advertisement

Assign a subset of values from a list returned by an iterable to a variable (Python)

I have an iterable that returns multiple values as a list. In this case, I only care about one of the values in each iteration.

As a concrete example:

class Triples:
 def __init__(self):
  self.x = 0
 def __iter__(self):
  return self
 def __next__(self):
  self.x += 1
  if (self.x < 5):
   return((self.x*2-1, self.x*2, self.x*2*-1))
  else:
   raise StopIteration

for x in Triples():
 print(x)

This produces:

(1, 2, -2)
(3, 4, -4)
(5, 6, -6)
(7, 8, -8)

But I really just want:

2
4
6
8

I could just do:

for x in Triples():
 print(x[1])

or:

for x in Triples():
 y = x[1]
 print(y)

or:

for (_,x,*_) in Triples():
 print(x)

but it feels like it would be nicer to tell Python that I’m uninterested in all the other values instead of storing them in a place I won’t look. (And adding a del(_) feels both too late and too aggressive.)

In particular, this doesn’t work:

for x in Triples()[1]:
 print(x)

(It errors out with TypeError: 'Triples' object is not subscriptable; note that that makes this related question‘s highest-rated answer incorrect for this case.)

and this:

for x in list(Triples())[1]:
 print(x)

Does the wrong thing. It generates the whole list of lists and then returns the second list (instead of the second element of each list), and produces:

3
4
-4

Is there something better or should I just do one of the things I already mentioned?

Advertisement

Answer

The solutions you already have are likely the clearest ones available.

I’d go for either indexing:

for x in Triples():
    print(x[1])

Or unpacking with dummy names:

for _, x, _ in Triples():  # no need for parentheses or *_ here
    print(x)

But if neither of those are satisfying to you, there are a few more options. If you really needed to get a list or iterator of just the second values from the iterator, you could write a list comprehension or generator expression, separate from the loop:

second_values = (x for _, x, _ in Triples())

for x in second_values:
    print(x)

For a finite iterator, you could also play around with zip(*Triples()), to transpose your data so you can index it all at once to get the second values from every result:

for x in list(zip(*Triples()))[1]:
    print(x)

That is not be very easily understandable though, and consumes the entire iterator up-front, rather than lazily as the iteration happens.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement