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.