- Lab
- A Cloud Guru
Defining and Using Python Generators
Generators are Python functions that behave like iterators. By using generators, we're able to create sequences that are evaluated as each item is needed, making them more memory-efficient compared to simply having lists. In this hands-on lab, we'll be building a generator function that behaves like the built-in `range` function, except it will yield string characters instead of integers. To feel comfortable completing this lab, you'll need to know how to do the following: * Defining and using generators. Watch the "Defining and Using Generators" video from the Certified Entry-Level Python Programmer Certification course. * Working with `for` loops. Watch the "The `for` Loop" video from the Certified Entry-Level Python Programmer Certification course. * Using the `ord` and `chr` functions. Watch the "String Encodings and Functions" video from the Certified Entry-Level Python Programmer Certification course.
Path Info
Table of Contents
-
Challenge
Create the char_range Generator
The first thing that we need to create is the
char_range
function within theusing-generators.py
file. Just creating a function doesn't make it a generator. To do that, we need to use the keywordyield
. Let's create the function and simplyyield
the start character for the time being:using-generators.py (partial)
def char_range(start, stop, step=1): yield start
Now if we run the file, we should make it past the first check that ensures we've created a generator. You should see the following assertion error:
$ python3.7 using-generators.py Traceback (most recent call last): File "using-generators.py", line 23, in <module> ], f"Expected ['a', 'b', 'c', 'd', 'e'] but got {repr(list(char_range('a', 'e')))}" AssertionError: Expected ['a', 'b', 'c', 'd', 'e'] but got ['a']
Next, we'll need to create a range from our starting character to our ending character by utilizing the Unicode code point for each character. To do this we'll use the
ord
function, and then we'll yield the result of thechr
function and the code point for each item in our range. Here's how we might write this:using-generators.py (partial)
def char_range(start, stop, step=1): start_code = ord(start) stop_code = ord(stop) for value in range(start_code, stop_code, step): yield chr(value)
Now if we run this, we'll see the following error:
$ python3.7 using-generators.py Traceback (most recent call last): File "using-generators.py", line 27, in <module> ], f"Expected ['a', 'b', 'c', 'd', 'e'] but got {repr(list(char_range('a', 'e')))}" AssertionError: Expected ['a', 'b', 'c', 'd', 'e'] but got ['a', 'b', 'c', 'd']
We're creating the range, but we're not yet being inclusive of the stop character.
-
Challenge
Include the Stop Character in the Results
Now that we're yielding characters properly, we need to deviate from how the
range
function works to also include the stop character's code point. To do this, we'll add1
to the stop character when we are creating ourrange
:using-generators.py (partial)
def char_range(start, stop, step=1): start_code = ord(start) stop_code = ord(stop) for value in range(start_code, stop_code + 1, step): yield chr(value)
Now when we run the file we should see the following error:
$ python3.7 using-generators.py Traceback (most recent call last): File "using-generators.py", line 37, in <module> ], f"Expected ['e', 'd', 'c', 'b', 'a'] but got {repr(list(char_range('e', 'a')))}" AssertionError: Expected ['e', 'd', 'c', 'b', 'a'] but got []
This error is caused by us not being able to create a range from a higher number to a lower number while using a positive step value.
-
Challenge
Support the Start Code Point Being More Than the Stop Code Point
To support creating a range from a higher Unicode code point to a lower one, we'll need to make our step value negative if we see that the code point for
start
is greater than the code point forstop
. We can do this by using a conditional and reassigning thestep
variable:using-generators.py (partial)
def char_range(start, stop, step=1): start_code = ord(start) stop_code = ord(stop) if start_code > stop_code: step *= -1 for value in range(start_code, stop_code + 1, step): yield chr(value)
Running the file again we can see that this almost works:
$ python3.7 using-generators.py Traceback (most recent call last): File "using-generators.py", line 40, in <module> ], f"Expected ['e', 'd', 'c', 'b', 'a'] but got {repr(list(char_range('e', 'a')))}" AssertionError: Expected ['e', 'd', 'c', 'b', 'a'] but got ['e', 'd', 'c']
The issue here is that we're always adding a positive 1 to our
stop
character's code point — even if we're stepping in a negative direction. To get around this, we can store the value that we add to thestop_code
in a variable and make it negative if we need to negate thestep
value. Here's what this code change looks like:using-generators.py (partial)
def char_range(start, stop, step=1): stop_modifier = 1 start_code = ord(start) stop_code = ord(stop) if start_code > stop_code: step *= -1 stop_modifier *= -1 for value in range(start_code, stop_code + stop_modifier, step): yield chr(value)
Now the
stop_modifier
variable will adjust based on our step direction. Running the file one last time, we should see no output at all, which indicates thatchar_range
meets all of the predefined expectations.
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.