Day 7: Generators
Tags: python, beginners, programming
Published: 2023-02-16 16:00:00


## What I learnt

- Generators
- Range
- Yield
- Generator Expressions
- Generator Functions
- Generator Comprehensions

## Generators

Generators in Python are functions that allow you to iterate over a sequence of values without creating a list in memory. Generators are used to create iterators, but with a different approach than the usual functions. Instead of returning all the values at once, generators return them one at a time, which makes them more memory efficient.
In simple terms; generators are a special type of function that return an iterable set of items, one at a time, in a special way.

Generator functions are functions that return a generator object some examples of generator functions are range(), enumerate() etc.

- range() - This is a built-in generator function that takes in two parameters (start and stop) and returns an iterator object that can be used to iterate over the sequence of numbers from start to stop.
- enumerate() - This is another built-in generator function that takes in an iterable object and returns an iterator object with index-value pairs for each element in the given iterable object.
- yield - The yield keyword is used inside generator functions to return values one at a time instead of returning all the values at once.

### Example

```python
num_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

def square_numbers(nums):
    result = []
    for i in nums:
        result.append(i * i)
    return result

square_num = square_numbers(num_list)
print(square_num)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
```

The above code is returning a list and it is not memory efficient. We need to use generators to solve this problem.

```python
def square_numbers(nums):
    for i in nums:
        yield i * i

square_num = square_numbers(num_list)
<generator object square_numbers at 0x7f9b8c0b9f50>
print(square_num)
print(next(square_num))  # This will return the next value in the generator object until there are no more values to return.
```

Instead of returning a list, the above code is returning a generator object. We can iterate over the generator object to get the values one at a time.

To get all the values from the generator object, we can use a for loop.

```python
for num in square_num:
    print(num)  # 1 4 9 16 25 36 49 64 81 100
```


## Python Generators vs List Comprehension

- List Comprehension: is a concise way to create lists in Python. It is a very powerful tool that can be used to create lists in a very efficient way.

### Example

```python
squares = [i * i for i in range(1, 11)]
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

```

- Generators: are a special type of function that return an iterable set of items, one at a time, in a special way. Generators are memory efficient and are used to create iterators.

```python
def square_numbers(nums):
    for i in nums:
        yield i * i

square_num = square_numbers(range(1, 11))
 print(square_num)  # <generator object square_numbers at 0x7f9b8c0b9f50>

for num in square_num:
    print(num)  # 1 4 9 16 25 36 49 64 81 100

```

> Using list comprehension

```python
squares = [i * i for i in range(1, 11)]
print(squares)  # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
```
>  Using a generator expression
```python
squares = (i * i for i in range(1, 11))
print(squares)  # <generator object <genexpr> at 0x7f9b8c0b9f50>
```

### Exercise:

- Write a generator function that takes in two numbers (start and end) as parameters and yields all the even numbers between start and end (inclusive).

```python
def even_numbers(start, end):
    for i in range(start, end + 1):
        if i % 2 == 0:
            yield i

my_even_numbers = even_numbers(1, 50)
for num in my_even_numbers:
    print(num)

```

- Write a generator function that takes in two numbers (start and end) as parameters and yields all the prime numbers between start and end (inclusive).

```python
def prime_numbers(start, end):
    for i in range(start, end + 1):
        if i > 1:
            for j in range(2, i):
                if i % j == 0:
                    break
            else:
                yield i


my_prime_numbers = prime_numbers(1, 50)
for num in my_prime_numbers:
    print(num)

```
## Conclusion

In this article, we learnt about generators and how to use them. We also learnt about how much more efficient generators are compared to lists. So the next time you need to create a list of values, you should consider using generators instead of list comprehensions.

## Resources

- You can learn more on [Python Generators](https://realpython.com/introduction-to-python-generators/) here.
- [Corey Schafer](https://www.youtube.com/watch?v=bD05uGo_sVI) has a great video on generators. I highly recommend you to watch it.

Please contact me through [Twitter](https://twitter.com/jobizil) or [LinkedIn](https://www.linkedin.com/in/job-ugbem/) if you have any questions or recommendations.

Additionally, do leave a comment below if you have any queries or recommendations. I'd love to hear from you, and please share this post with your friends and subscribe to [Dev.to](https://dev.to/jobizil) for more articles like this.
Thank you for taking the time to read this.