How to use Iterables' in python

When working with Python, you might often hear the term "iterables," but what exactly are they, and why should you care? In this article, we'll break down the concept of iterables, explore their significance in memory management, delve into their implementation using the __iter__ function in a class, and showcase their utility through examples and built-in functions.

What Are Iterables?

Iterables are a fundamental concept in Python that allow us to work with a sequence of values, one at a time. In simpler terms, they help us deal with a bunch of items without having to load them all into memory at once. This is a game-changer when handling large data sets or when you want to conserve memory resources.

I still don't understand!

Iterables can be most likened to lists. They provide the same concept for multiple values to be used or looped through.

Why do we use them? It is because of their memory and time efficiency.

Take a look at this! If you were to use a list of 1 hundred numbers, Creating it as a list will eat up memory like crazy. What we can do to avoid that is use the range function.
This built-in function returns an iterable that will only take it one value at a time and discard every previous value.

Let's see how it will work.

Using the __iter__ Function

Now, let's talk about the inner workings of iterables. In Python, classes can be made iterable by defining the __iter__ method. This method returns an iterator object, which is responsible for keeping track of the current position and providing the next value when requested.

class MyIterable:
    def __init__(self, *data):
        self.data = data

    def __iter__(self):
        self.index = 0
        return self

    def __next__(self):
        if self.index < len(self.data):
            value = self.data[self.index]
            self.index += 1
            return value
        else:
            raise StopIteration

In this example, the MyIterable class takes a list of data during initialization. The __iter__ method initializes the index to 0, and the __next__ method returns the next value while updating the index until all values are exhausted.

This __next__ function can be modified to do anything apart from just changing the values. In that case, you can run a function only when you get to that particular value of the iterable.

What makes this more memory efficient because it takes it one value at a time and discards it after use

An Example of an implementation of the code above is shown as follows

x = MyIterable(1, 2, 4)
for i in x:
    print(i) # prints 1 2 4

Using the yield keyword

Here is how we can use the yield keyword to implement the same concept

def my_map(func, iterable):
    for item in iterable:
        yield func(item)

Here the function my_map is returning the the returned value of the function only when it is called.

The function is paused after every iteration and waits for the next call to return the value.

This is usually called a Generator in python.

Conclusion

Iterables are like a magical tool in Python that enable memory-efficient processing of sequences of data. By processing items one by one, you can tackle large datasets without overwhelming your computer's memory. Some built-in functions work using this concept like range, map, filter, sum and alot more. The next time you're working with a massive amount of data, remember that iterables are both memory and code efficient.