Preface
Do you know the difference between the following syntax?
- [x for x in range(5)]
- (x for x in range(5))
- tuple(range(5))
5 Facts About the Lists
First off, a short review on the lists (arrays in other languages):
What is List Comprehension?
Often seen as a part of functional programming in Python, list comprehensions allow you to create lists with a for loop with less code. Look at the implementation of the previous example using a list comprehension:
The above example is oversimplified to get the idea of syntax. The same result may be achieved simply using list(range(0, 19, 2)) function. However, you can use a more complex modifier in the first part of comprehension or add a condition that will filter the list. Something like this:
Another available option is to use list comprehension to combine several lists and create a list of lists. At first glance, the syntax seems to be complicated. It may help to think of lists as an outer and inner sequences. It’s time to show the power of list comprehensions when you want to create a list of lists by combining two existing lists.
Let’s try it with text or it’s correct to say string object.
The comprehensions are not limited to lists. You can create dicts and sets comprehensions as well:
Difference Between Iterable and Iterator
It will be easier to understand the concept of generators if you get the idea of iterables and iterators. Iterable is a "sequence" of data, you can iterate over using a loop. The easiest visible example of Iterable can be a list of integers - [1, 2, 3, 4, 5, 6, 7]. However, it’s possible to iterate over other types of data like strings, dicts, tuples, sets, etc.
Basically, any object that has iter() method can be used as an Iterable. You can check it using hasattr() function in the interpreter.
Iterator protocol is implemented whenever you iterate over a sequence of data. For example, when you use a for loop the following is happening on a background:
For example:
Generator Expressions
In Python, generators provide a convenient way to implement the iterator protocol. Generator is an iterable created using a function with a yield statement. The main feature of generator is evaluating the elements on demand. When you call a normal function with a return statement the function is terminated whenever it encounters a return statement. In a function with a yield statement the state of the function is “saved” from the last call and can be picked up the next time you call a generator function. e.g:
- def my_gen():
- for x in range(5):
- yield x
In terms of syntax, the only difference is that you use parenthesis instead of square brackets. However, the type of data returned by list comprehensions and generator expressions differs:
The main advantage of generator over a list is that it take much less memory. We can check how much memory is taken by both types using sys.getsizeof() method.
Note: in Python 2 using range() function can't actually reflect the advantage in term of size, as it still keeps the whole list of elements in memory. In Python 3, however, this example is viable as the range() returns a range object.
Generator yields one item at a time thus it is more memory efficient compared to the list. For example, when you want to iterate over a list, python reserves memory for the whole list. Generator won’t keep the whole sequence in memory and will only “generate” the next element of the sequence on demand.