1
00:00:00,000 --> 00:00:05,990
A generator object is an object that can be used in the context of an iterable,

2
00:00:06,000 --> 00:00:07,990
like for instance in a for loop.

3
00:00:08,000 --> 00:00:10,990
Let's take a look at how this is done in Python.

4
00:00:11,000 --> 00:00:17,990
We'll make a working copy of generator .py. I'll call it generator-working.py.

5
00:00:18,000 --> 00:00:24,990
We'll open that up and we see that here we have a range object being used in the

6
00:00:25,000 --> 00:00:27,990
context of an iterator.

7
00:00:28,000 --> 00:00:33,990
So o is getting assigned to this range object that's a range of 25 and it's

8
00:00:34,000 --> 00:00:38,990
being used in the context of this for loop which is an iterable context and if

9
00:00:39,000 --> 00:00:45,990
I run this, you see that we get series of numbers from 0 through 24 and this

10
00:00:46,000 --> 00:00:46,990
is how range works.

11
00:00:47,000 --> 00:00:53,990
Range is non-inclusive, which means that we ask for a range of 0 through 25.

12
00:00:54,000 --> 00:01:00,990
What we get 0 through 24, because it's up to but not including the range we specify.

13
00:01:01,000 --> 00:01:05,990
Range takes three possible arguments. It only requires 1 and so those three

14
00:01:06,000 --> 00:01:12,990
possible arguments are the start and the stop and the step.

15
00:01:13,000 --> 00:01:20,990
So we start at 0 and stop at 25 and step by 1, this is the result that we get.

16
00:01:21,000 --> 00:01:27,990
And so start and step default to 0 and 1 and all you have to specify is the stop.

17
00:01:28,000 --> 00:01:35,990
If I were to start it say at 5 and step by say 2, save that and run it,

18
00:01:36,000 --> 00:01:37,990
you will get 5 through 23.

19
00:01:38,000 --> 00:01:41,990
Again, we don't get 25 because it's not inclusive, and you see they are stepping by 2.

20
00:01:42,000 --> 00:01:48,990
So what I would like to do here is create our own range object as an exercise to

21
00:01:49,000 --> 00:01:51,990
learn how to create a generator object in Python.

22
00:01:52,000 --> 00:01:55,990
It will work exactly like this one, except that it will be inclusive.

23
00:01:56,000 --> 00:01:57,990
We'll always get that last number there.

24
00:01:58,000 --> 00:02:04,990
So go ahead and reset this to 25 and we will start creating our class.

25
00:02:05,000 --> 00:02:18,990
So we'll call this class inclusive_ range and it will have a constructor.

26
00:02:19,000 --> 00:02:26,990
And it will have an iterator.

27
00:02:27,000 --> 00:02:29,990
These are both special method names in Python.

28
00:02:30,000 --> 00:02:35,990
Init with the two underscores before and after it are for constructors and iter

29
00:02:36,000 --> 00:02:39,990
like this, with two underscores before it and two underscores after it, makes the

30
00:02:40,000 --> 00:02:42,990
object an iterable object.

31
00:02:43,000 --> 00:02:45,990
So this is where we'll put our generator function in here.

32
00:02:46,000 --> 00:02:51,990
The constructor will need to be able to do this weird thing with the argument.

33
00:02:52,000 --> 00:02:54,990
The first argument and the third argument are optional but the second

34
00:02:55,000 --> 00:02:56,990
argument is required.

35
00:02:57,000 --> 00:02:59,990
That's going to take a little bit of manipulation on our end and we'll do

36
00:03:00,000 --> 00:03:00,990
that in the constructor.

37
00:03:01,000 --> 00:03:02,990
We need this arbitrary list of positional arguments.

38
00:03:03,000 --> 00:03:09,990
So we'll use the list argument syntax here and then asterisk and args and

39
00:03:10,000 --> 00:03:13,990
we'll get the number of arguments by using the length built in.

40
00:03:14,000 --> 00:03:20,990
len(args) like that and if we have less than one argument we are going to

41
00:03:21,000 --> 00:03:25,990
raise an error.

42
00:03:26,000 --> 00:03:35,990
We'll use TypeError requires at least one argument. Let's say if numargs is

43
00:03:36,000 --> 00:03:47,990
less than 1 and we'll use elif numargs == 1, elif numargs == 2.

44
00:03:48,000 --> 00:03:51,990
I like to outline my code like this sometimes and then go back and fill it in.

45
00:03:52,000 --> 00:04:05,990
Numargs = 3 or else we'll raise a TypeError.

46
00:04:06,000 --> 00:04:10,990
Expected at most three arguments and

47
00:04:11,000 --> 00:04:15,990
got this other number.

48
00:04:16,000 --> 00:04:19,990
Now we can go ahead and fill in each of these conditions.

49
00:04:20,000 --> 00:04:22,990
If we get just one argument then we know that's the stop.

50
00:04:23,000 --> 00:04:38,990
So we can say self.stop = args sub 0. Otherwise we have got self.start = 0 and

51
00:04:39,000 --> 00:04:45,990
self.step = 1, because those are those default values.

52
00:04:46,000 --> 00:04:49,990
If we have two arguments and we know that that's the start and the stop.

53
00:04:50,000 --> 00:05:02,990
self.start, self.stop, and we assign that to args and we use our default for step.

54
00:05:03,000 --> 00:05:06,990
Finally, if we have three arguments, then we know that's all of them,

55
00:05:07,000 --> 00:05:14,990
start, stop and step.

56
00:05:15,000 --> 00:05:19,990
So now we have our constructor and our iterator is very easy.

57
00:05:20,000 --> 00:05:28,990
We start by setting the starting point and we have a simple while loop, while i

58
00:05:29,000 --> 00:05:37,990
is less than or equal to a stop, then we yield the result-- and we'll get back to that one--

59
00:05:38,000 --> 00:05:44,990
and we increment the iterator by this step.

60
00:05:45,000 --> 00:05:50,990
So this is what makes it a generator is the yield statement.

61
00:05:51,000 --> 00:05:53,990
Yield works just like return but with the significant difference.

62
00:05:54,000 --> 00:05:57,990
If I were to use return here, it would return the value and the next time the

63
00:05:58,000 --> 00:06:00,990
iterator was called it would start at the beginning of the function.

64
00:06:01,000 --> 00:06:05,990
By using yield instead it returns the value and the next time the function is

65
00:06:06,000 --> 00:06:09,990
called execution picks up right after the yield statement.

66
00:06:10,000 --> 00:06:14,990
So the way this will run is it will set the starting point and it will test

67
00:06:15,000 --> 00:06:19,990
the while loop and assuming that the starting point is less than or equal to

68
00:06:20,000 --> 00:06:24,990
the stop point, it will yield the value. Do that's the starting point and then

69
00:06:25,000 --> 00:06:29,990
the next time the iterator is called, it will increment that value and test

70
00:06:30,000 --> 00:06:34,990
the while loop again, and then it will yield the next value and then it will

71
00:06:35,000 --> 00:06:39,990
increment and we'll yield the next value and this allows it to operate as an iterator.

72
00:06:40,000 --> 00:06:45,990
So by having the yield statement inside the function, that makes the function a

73
00:06:46,000 --> 00:06:49,990
generator and what a generator generates is an iterable object.

74
00:06:50,000 --> 00:06:57,990
We need to change this to our inclusive_range. I have a little typo here.

75
00:06:58,000 --> 00:07:05,990
self.step. Save that and run it and there we have a range all the way up to and

76
00:07:06,000 --> 00:07:13,990
including our stop, and so let's go ahead and test out our constructor.

77
00:07:14,000 --> 00:07:19,990
I'll change the start point to 5 and now it start to 5 and go up to 25.

78
00:07:20,000 --> 00:07:25,990
Save that and run it, and there it, starts at 5 and goes all the way up to 5.

79
00:07:26,000 --> 00:07:32,990
Let's have it step by 2 instead of stepping by 1. Save that and run it.

80
00:07:33,000 --> 00:07:40,990
Now we see it steps by 2. We can change that to 7 and it will still do what we expect.

81
00:07:41,000 --> 00:07:46,990
That's great and now let's call it with no arguments and we'll test our error conditions.

82
00:07:47,000 --> 00:07:50,990
No arguments, we would expect it to get that TypeError and there it is,

83
00:07:51,000 --> 00:07:54,990
TypeError requires at least one argument and if we give it four arguments, let's say

84
00:07:55,000 --> 00:08:05,990
1, 25, and 3 and 14, we expect to get this other type error here and there it is,

85
00:08:06,000 --> 00:08:08,990
expected at most three arguments and got four.

86
00:08:09,000 --> 00:08:14,990
So we have successfully duplicated the range generator function, save that and

87
00:08:15,000 --> 00:08:17,990
run it, except that ours is inclusive.

88
00:08:18,000 --> 00:08:18,990
That was really easy to do.

89
00:08:19,000 --> 00:08:25,990
So this is how you make a generator object is by using the iter method in your

90
00:08:26,000 --> 00:08:29,990
class and now your object becomes iterable.

91
00:08:30,000 --> 00:08:35,990
It becomes a generator and when the object is used in the context of an

92
00:08:36,000 --> 00:08:41,990
iterable, like for instance, in this for loop, then that iter methods gets called

93
00:08:42,000 --> 00:08:47,990
automatically and you do not have to do something like .iter.

94
00:08:48,000 --> 00:08:50,990
You can simply use the object in that context.

95
00:08:51,000 --> 00:08:56,990
In fact I don't even have to create an intermediate variable.

96
00:08:57,000 --> 00:09:01,990
So I can take this line out all together and put it in this place here and you

97
00:09:02,000 --> 00:09:05,990
will see that range is used like this very often.

98
00:09:06,000 --> 00:09:11,990
So I can save that and run it and there we have a drop in replacement for

99
00:09:12,000 --> 00:09:17,990
range except that this one is inclusive and that's how you make the generator object in Python.

100
00:09:18,000 --> 00:09:21,990
It's a very convenient and very powerful technique.

101
00:09:22,000 --> 00:09:25,990
You will find yourself using it in some places that you won't anticipate.

102
00:09:26,000 --> 00:09:29,990
I have used it in database applications where I have a very specific database

103
00:09:30,000 --> 00:09:35,990
application with a specific class that simply steps through a particular type of a query.

104
00:09:36,000 --> 00:09:41,990
I have used it in parsing files, where I want to get a certain pattern out of a file.

105
00:09:42,000 --> 00:09:44,990
There is all kinds of applications for this and you will find that you actually

106
00:09:45,000 --> 00:09:55,000
use it more often than you might think.

