Lecture
22 Advanced Python Topics II
We'll have more
examples about Iterators, Generators, and Decorators for Python:
1. Iterators:
As
we learned in last lecture, the data has to be converted into a
iterator to work with the 'next()' function to iterate through all
elements in the data:
The following one works:
If it is out of range,
an error message pops up:
Python
iterators implement iterator protocol which consists of two special
methods __iter__() and __next__(). The __iter__() method returns an
iterator object where as __next__() method returns the next element
from the sequence. There
is the __iter__() method working behind the scene for the iteration.
But __iter__() alone cannot iterate through all items as we need to
move to next item for iteration. So __next__() function is used for
that.
Here is an example to build our own iterator to display odd number from
1 to the max number supplied as the argument.
In
the example below, it seems like iterate with an object doesn't make
any sense. But I defined the iterator inside the class for how to
iterate an object instantiated from this class.
The
'for loop' didn't show you what is being ran but behind the scene, it
invokes the __iter__ method and the __next__ method. I defined the
__inter__ method and the __next__ method in the class OddNum to
override the what is suppposed to be ran.
Let’s use a Python built-in function 'dir()' to find out all the
associated attributes of the iterable x.
There
is the __iter__() method working behind the scene for the iteration.
But __iter__() alone cannot iterate through all items as we need to
move to next item for iteration. So __next__() function is used for
that.
2. Generators
To
clarify the confusion, let's look at the following example again. When
you iterate through a generator function, it will be ran until the
first 'yield' and stop at there. This will return the 'yielded' value
but won't terminate the function.
'yield n' actually returned 'n'. If I print the 'next(a)' funciton,
then you will see 'n' will be printed.
You've to use two more 'next()' functions to print all the n's.
Keep in mind that the following print function won't work (due to the
absence of the 'next()' function):
However,
if you have at least one 'next()' function, the while loop will at
least be ran until the first iteration and stop after the first
'yield'.
Instead, you need a for loop to print out all the intermediate values
(the 'for loop' has many 'next()' functions inside).
As we mentioned in the last lecture, using
the 'Generator Expression as follows, the generator expression did not
produce the required result immediately. Instead, it returned a
generator object with produces items on demand.
Generators can
be implemented in a clear and concise way as compared to their iterator
class counterpart.
Generator
Expressions:
Like a list,
generators can also be written in the same
manner except they return a generator object rather than a list:
Take
note of the parentheses on either side of the second line denoting a
generator expression, which, for the most part, does the same thing
that a list comprehension does, but does it lazily: The generator
expression takes less size of the memory:
The
'sys.getsizeof' function returns the memory consumption.
3.
Closure and Decorator Examples
An
example: The result of calling say_hello is passed into the capitalize
decorator. The decorator modifies the say_hello function by changing
its result to uppercase. We see that capitalize decorator takes in a
callable(say_hello) as an argument and returns another
callable(uppercase). This is just a basic example on decorator
One more
example:
The following
one is a simple decorator example that using a wrapper to check if the
input is a certain data type.
However, if the
returned data is a dictionary:
If the
dictionary has more than 1 entires: