Join Bill Weinman for an in-depth discussion in this video Creating a sequence with a generator function, part of Python 3 Essential Training.
- View Offline
A generator function is a function that returns an iterator object. So this is how you create functionality that can be used in a for loop or any place an iterator is allowable in Python. So let's go ahead and create a generator object. We will make a working copy of generator.py, and we'll go ahead and open that up. And here we have an example of using the range object.
And if we run this, you'll notice that we get our range of numbers up to and not including the 25, because the range object is naturally noninclusive. So a lot of times you might want an inclusive range object. So let's go ahead and create one. We'll call this inclusive_range, and we'll define a function. Call it inclusive_range. And the range object takes three arguments: start, stop, and step.
And we'll go ahead and say i=start, while i<=stop, and that will make it inclusive. i+=step. And then just before we increment, we're going to return, although we're not going to use return. We're going to yield i. What that does is it returns I, but because we're using yield instead of return, the next time the function is called execution will continue right after the yield statement.
So the next thing that will happen is i will get incremented by step and then the loop will be tested again. And then assuming that the loop condition is still true, yield will be called again and it will yield another iteration in the sequence. So again, what makes the yield different than return is that as the function gets called over and over again, each time execution begins right after the yield and continues as if the function were running continually.
And yield returns each time the next item in the sequence. So let's go ahead and run this. In order to run it, we are going to need to give it a start and a step. So we'll start at 0, and we'll step by 1, and we should get the same sequence, but including a 25 at the end. Save it and run it, and there we are, got the same sequence, and there is our 25 at the end. So that is how yield works and that is how you create a generator.
Let's go ahead and exercise all the techniques we've been learning in this function chapter and make this function work exactly like range does. Range can be called with one, two, or three arguments. If it's called with just one argument, stop is the value that's used and start is given a default value of 0 and step is given a default value of 1. But Python doesn't really have a way to do that. If I give this one here a default value of 0, I say start=0, and I give step a default value of 1, and I call this with just the 25 by itself.
Save that and run it. We get this SyntaxError. Non-default argument follows default argument. So this is not allowable in Python. The only way we can really do this is with a tuple. So let's go ahead and process our arguments. Let's start by taking a number of arguments, because we are going to different things with different numbers of arguments. numargs = the length of tuple, and if numargs<1, we're going to raise an error.
Requires at least one argument. And then elif numargs == 1, and we'll just set these up, elif numargs == 2, elif numargs == 3, or else, and we'll raise another error. TypeError, inclusive_range expected at most 3 arguments, got some number, format (numargs)).
Now I've got this little x here because it's expecting something to be in that suite. So if there's just one argument, then start is going to be 0 and step is going to be 1, and we'll go up to the top of the list, and we'll use our argument. We'll say stop=args0. All right. And if we have two arguments, then our arguments are start and stop, and we can assign those like this and give step the default value.
If we have three arguments, and we can assign them all like this. start, stop, and step=args. All right. Now, this should work. Let's save it and see how we are calling it up here? With just the 25 by itself. So we'll run it, and there we have all 25 numbers, just like we expected. It works just like range, except it's inclusive and it gives us that last number. If we have a 5 at the beginning of it, it should give us 5 through 25, there we go.
And if we step by three, let's say, save that and run it. That's exactly what we expect. Let's say we have too many arguments. Let's give it some other number. Now we're going to expect to get this one here. It's not less than 1. It's not 1. It's not 2. It's not 3. It's this something else. So save that and run it. And we get inclusive_range expected at most 3 arguments, and got 4.
And let's trigger this error here and give it no arguments at all. Save that and run it. And requires at least one argument is our error message. So there we have it. We've duplicated the functionality of the range object. We've got a function that returns an iterator object and it does exactly what range does, except that it's inclusive. And we've accomplished this using the yield statement, which turns our function into a generator, and the way the yield works is each time yield is run, it returns the value.
And the next time the function is called, execution picks up from right after the yield. And this turns the function into a generator and what it generates is an iterator object, which can be used exactly like any iterator object in Python.
- A Python 3 quick start for experienced developers
- Creating functions and objects
- Using Python's built-in objects and classes
- Repeating code with loops and iterators
- Understanding and using conditional expressions
- Creating sequences with generators
- Reusing code with objects and libraries
- Handling errors with exceptions