0

I created my own iterator (to learn how they work) as follows

class Reverse():
    def __init__(self, word):
        self.word = word
        self.index = len(word)
    def __iter__(self):
        return self
    def __next__(self):
        self.index -=1
        if self.index < 0:
            raise StopIteration
        return self.word[self.index]

print (char for char in Reverse("word"),end="")

I know I can say:

rev = Reverse("word")
for char in rev:
    print(char, end="")

but I was hoping to be able to do it in a single line

print (char for char in Reverse("word"),end="")

This doesn't work and raises an error. If I remove the end="" it prints out a generator object. Surely by including a for loop in the print statement it should iterate through my generator object and print each item? Why does this not happen

ForceBru
  • 43,482
  • 10
  • 63
  • 98
EML
  • 395
  • 1
  • 6
  • 15

4 Answers4

2

A generator is a lazy iterator. When you use a for loop, you are manually exhausting the generator. When you print it out in a single line, you are not exhausting it by iterating.

You can unpack the generator to exhaust it and get all of the values:

print(*(char for char in Reverse('word')), sep='')

I’ve changed end for sep as you are doing everything in 1 print call so want to separate each ‘argument’ (character) with no space.

You can also do the following:

print(*Reverse('word'), sep='')

As Reverse is already an iterator, you are able to exhaust it.

N Chauhan
  • 3,407
  • 2
  • 7
  • 21
1

Surely by including a for loop in the print statement it should iterate through my generator object and print each item?

No, it will print the string representation of a generator object.

If you want to output what this object generates, you can use a starred expression:

print(*(char for char in Reverse("word")),end="")

This is because the for loop in a generator expression isn't actually executed at the instantiation of the generator object, and that's the whole point of generators: you can have an infinite generator and still be able to pass it around as a regular value and consume finite chunks of it, all that without having infinite amounts of memory.

ForceBru
  • 43,482
  • 10
  • 63
  • 98
1

Why not like this?

print(''.join([char for char in Reverse("word")]))

  • 1
    The `[ ]` create an unnecessary list, and you also do not have to iterate manually. Just do `print(''.join(Reverse("word")))` – DeepSpace May 27 '19 at 20:02
  • 1
    @DeepSpace it's faster to join the list comprehension than a genex. I need to dig the reference out, but the genex will be expanded before `join` can work – roganjosh May 27 '19 at 20:06
  • 1
    @DeepSpace https://stackoverflow.com/questions/41519707/how-does-python-optimize-conditional-list-comprehensions – roganjosh May 27 '19 at 20:07
  • @roganjosh It sounds a bit weird but I'll wait for the reference. Anyway, for longer "words" I'm pretty sure the overhead of the list comp will be more noticeable. – DeepSpace May 27 '19 at 20:07
  • @DeepSpace more-specifically, [this](https://stackoverflow.com/a/9061024/4799172) but I guess you've probably already followed the rabbit hole and found that :) – roganjosh May 27 '19 at 20:14
1
x = my_objects.objects.all() # x, turns to a QuerySet

for i in range(len(x)):
    print(x[i])
Robo
  • 19
  • 5