> For the complete documentation index, see [llms.txt](https://ricardomol.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://ricardomol.gitbook.io/notes/backend/python/generators.md).

# Generators

## Iterators

Object that enables a programmer to traverse a container, particularly lists. However, an iterator performs traversal and gives access to data elements in a container, but does not perform iteration. You might be confused so lets take it a bit slow. There are three parts namely:

* Iterable
* Iterator
* Iteration

All of these parts are linked to each other.

### 3.1. Iterable

Any object in Python which has an `__iter__` or a `__getitem__` method defined which returns an **iterator** or can take indexes (You can read more about them [here](https://stackoverflow.com/a/20551346)).&#x20;

Any object which can provide us with an **iterator**.

### 3.2. Iterator

Any object in Python which has a `next` (Python2) or `__next__` method defined.&#x20;

### 3.3. Iteration

The process of taking an item from something e.g a list.

### 3.4. Generators

Generators are **iterators, but you can only iterate over them once**

**They do not store all the values in memory, they generate the values on the fly**.

You use them by iterating over them, either with a ‘for’ loop or by passing them to any function or construct that iterates.

Most of the time `generators` are implemented as functions. However, **they do not `return` a value, they `yield`it**.

Example of a `generator` function:

```python
def generator_function():
    for i in range(10):
        yield i

for item in generator_function():
    print(item)

# Output: 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9
```

Generators are **best for calculating large sets of results (particularly calculations involving loops themselves) where you don’t want to allocate the memory for all results at the same time**.

Example calculates fibonacci numbers:

```python
# generator version
def fibon(n):
    a = b = 1
    for i in range(n):
        yield a
        a, b = b, a + b
```

Now we can use it like this:

```python
for x in fibon(1000000):
    print(x)
```

This way we would not have to worry about it using a lot of resources.&#x20;

&#x20;We can iterate over `generators` only once but we haven’t tested it.&#x20;

**`next()`: allows us to access the next element of a sequence**. So let’s test out our understanding:

```python
def generator_function():
    for i in range(3):
        yield i

gen = generator_function()
print(next(gen))
# Output: 0
print(next(gen))
# Output: 1
print(next(gen))
# Output: 2
print(next(gen))
# Output: Traceback (most recent call last):
#            File "<stdin>", line 1, in <module>
#         StopIteration
```

**After yielding all the values `next()` caused a `StopIteration` error (**&#x61;ll the values have been yielded).

Do you know that a few **built-in data types in Python also support iteration**? Let’s check it out:

```python
my_string = "Yasoob"
next(my_string)
# Output: Traceback (most recent call last):
#      File "<stdin>", line 1, in <module>
#    TypeError: str object is not an iterator
```

The error says that **`str` is not an iterator. Well it’s right! It’s an iterable but not an iterator. This means that it supports iteration but we can’t iterate over it directly.** So how would we iterate over it? It’s time to learn about one more **built-in function, `iter`**. It returns an `iterator` object from an iterable. While an `int` isn’t an iterable, we can use it on string!

```python
int_var = 1779
iter(int_var)
# Output: Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
# TypeError: 'int' object is not iterable
# This is because int is not iterable

my_string = "Yasoob"
my_iter = iter(my_string)
print(next(my_iter))
# Output: 'Y'
```
