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.