# Generators & yield — Python

Source: https://www.geekswithgeeks.com/en/python/py-generators

> Functions that produce values lazily, one at a time, saving memory.

## A generator function

`yield` pauses a function and hands back a value; the function resumes right there on the next call — no need to build the whole list in memory.

```python
def count_up_to(n):
    i = 1
    while i <= n:
        yield i
        i += 1

for num in count_up_to(5):
    print(num)          # 1 2 3 4 5, one at a time
```

## Generator expressions

A **generator expression** looks like a list comprehension with `()` instead of `[]`, and produces values lazily too.

```python
squares = (n * n for n in range(1_000_000))  # nothing computed yet
print(next(squares))   # 0
print(next(squares))   # 1
print(sum(n * n for n in range(10)))   # 285
```

**Quiz:** Why prefer a generator over a list for a huge sequence?

- [ ] It runs faster in every case
- [x] It produces items lazily instead of storing them all in memory
- [ ] It automatically sorts the items

*Answer:* It produces items lazily instead of storing them all in memory. Generators compute one value at a time on demand, so huge or infinite sequences don't need to fit in memory.
