1

What is the proper difference between doing yield i from an iteration and return (i for i in range(10)).

def generator1():
    for i in range(10):
        yield i

def generator2():
    return (i for i in range(10))

For example, see these functions generator1() and generator2() both are differently written but both return a generator.

Outputs of IDLE:-

>>> generator1()
>>> <generator object generator1 at 0x107870468>

>>> generator2()
>>> <generator object generator2.<locals>.<genexpr> at 0x107870db0>

>>> import sys

>>> sys.getsizeof(generator1())
>>> 88
>>> sys.getsizeof(generator2())
>>> 88

As we can tell generator2() has fewer LOC (line of code) than generator2 and also the size of the object is the same, I've some questions.

  • What is the difference between both functions?
  • What does <genexpr> means when printing generator2()?
  • Which is the more suitable and efficient way of creating a generator?
Saad
  • 3,340
  • 2
  • 10
  • 32
  • 3
    Interesting question, but the title of your question doesn't quite make sense. `(i for i in range(10))` is a [generator expression](https://www.python.org/dev/peps/pep-0289/), not a "tuple iteration". – John Coleman May 30 '20 at 16:42
  • 3
    Does this answer your question? [Python: generator expression vs. yield](https://stackoverflow.com/questions/1995418/python-generator-expression-vs-yield) – Mustafa Aydın May 30 '20 at 16:45
  • @JohnColeman: Sorry, I don't know what can be the exact title to this question. if we do `return tuple(i for i in range(10))` then it is tuple iteration. – Saad May 30 '20 at 16:46
  • No, in this case you just return a tuple, which was instantiated from a generator. – Marat May 30 '20 at 16:47
  • 2
    But if you introduce `tuple()` like that you are fundamentally modifying the function is such a way that it would no longer be similar to the first one. Don't get hung up on the round brackets in generator expressions. They have nothing to do with tuples. – John Coleman May 30 '20 at 16:47
  • 1
    `` means [generator expression](https://docs.python.org/3/glossary.html#term-generator-expression) – wjandrea May 30 '20 at 16:49
  • Obviously, the simplest and best expression in this case would just be `range(10)`. All the "decorations" around it may make some point, but only lose efficiency. Of course, this is assuming a recent version of Python. – Amitai Irron May 30 '20 at 17:04
  • 1
    @Amitai `range` is just a dummy iteratable. If you wanted something a little more juicy, you could do for example, `math.sqrt(i) for i in range(0, 10)` – wjandrea May 30 '20 at 17:13
  • 1
    @wjandrea - agreed (obviously). However, seeing `(i for i in range(10))` being discussed, without mention that it is actually equivalent to `range(10)`, just with extra steps, prompted me to comment as I did. – Amitai Irron May 30 '20 at 17:20

1 Answers1

3

The difference is where the generator is defined. generator1 is a special generator function, because it contains a yield statement. generator functions always return generators. The generator is defined when you invoke generator1. generator2 is a regular function that uses a generator expression to construct a generator, and then returns it. The generator is defined when the line (i for i in range(10)) is executed. But if you add more logic, generator2 can return anything else, like None. For example:

def generator2(do_generator):
    if do_generator:
        return (i for i in range(10))
    else:
        return "I quit"

You can't do anything like that with generator1. It cannot return anything except a generator.

<genexpr> is short for generator expression. In your case, that's (i for i in range(10)). Generator expressions are very similar to list comprehensions, but they produce generators rather than lists.

Aaron Bentley
  • 1,332
  • 8
  • 14