range() vs enumerate()
In this lesson, you’ll learn how to use range()
and enumerate()
to solve the classic interview question known as Fizz Buzz.
range()
is a built-in function used to iterate through a sequence of numbers. Some common use cases would be to iterate from the numbers 0 to 10:
>>> list(range(11))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
To learn more, check out The Python range() Function.
enumerate()
is a built-in function to iterate through a sequence and keep track of both the index and the number. You can pass in an optional start parameter to indicate which number the index should start at:
>>> list(enumerate([1, 2, 3]))
[(0, 1), (1, 2), (2, 3)]
>>> list(enumerate([1, 2, 3], start=10))
[(10, 1), (11, 2), (12, 3)]
To learn more, check out Use enumerate() to Keep a Running Index.
Here’s the solution in the video:
for i, num in enumerate(numbers):
if num % 3 == 0:
numbers[i] = "fizz"
if num % 5 == 0:
numbers[i] = "buzz"
if num % 5 == 0 and num % 3 == 0:
numbers[i] = "fizzbuzz"
You could also solve the question using only one if
condition:
for i, num in enumerate(numbers):
if num % 5 == 0 and num % 3 == 0:
numbers[i] = "fizzbuzz"
elif num % 3 == 0:
numbers[i] = "fizz"
elif num % 5 == 0:
numbers[i] = "buzz"
Both are valid. The first solution looks more similar to the problem description, while the second solution has a slight optimization where you don’t mutate the list potentially three times per iteration.
In the video, you saw the interactive Python Terminal iPython. It has color coating and many useful built-in methods. You can install it.
00:00
In this video, you will learn about two different built-in functions, range()
and enumerate()
. Let’s get started with a real-world coding question called FizzBuzz.
00:11
So, fizz_buzz()
is a function that takes in a list of integers and replaces all integers that are evenly divisible by 3
with the string "fizz"
, replaces all integers divisible by 5
with the string "buzz"
, and replaces all integers divisible by both 3
and 5
with the string "fizzbuzz"
.
00:30
So, here is the example. We have a list [
45,
22,
14,
65,
97,
72]
.
00:36
We call fizz_buzz()
on that list, and it mutates that list and replaces the numbers according to these rules. So, 45
is divisible by both 3
and 5
, so it gets replaced with the string "fizzbuzz"
. 22
and 14
are not divisible by either 3
or 5
.
00:55
65
is divisible by 5
—"buzz"
. 97
is not divisible. 72
is divisible by 3
and we get the string "fizz"
.
01:06
So, let’s try to code out the solution using the built-in function range()
. So, range()
takes in a number. Given a list, we have to get the length of the list and pass that in, and it will iterate through the numbers 0
until whatever you pass in.
01:24
Let’s run this file interactively to see how range()
works. So, --no-banner
, just to remove some of the output at the top, and range_vs_enumerate.py
.
01:35
Cool. Let’s call fizz_buzz()
on just a random list, [1, 2, 3]
. We get 0
1
2
. That’s because the length of the list is 3
, and so we iterate through 0
, 1
, and 2
, not including 3
.
01:51
So, how can we use range()
to solve our problem? Well, we can get that number at that index, numbers[i]
, and then we do our checks here. if num % 3
—so, that will check if it’s divisible, because %
(modulo) gets the remainder and if there is no remainder, then it is divisible by 3
.
02:11
Then, we mutate our numbers
, at index i
, equal to the string "fizz"
. Let’s copy this for 5
.
02:26
Then our last case is if it’s both divisible by 3
and divisible by 5
—
02:34
then, we get numbers[i] = "fizzbuzz"
. So, let’s run this file interactively. Copy this,
02:51
numbers
. Cool! That output looks correct. Before we move on, let’s just quickly go over the doctest
module in Python. So, it’s a really nice module that we will actually go over more in detail in a couple videos, but basically when you run python3 -m doctest
and then the filename, it will look at all your functions, look at the docstring, look at the doctests, and then run them and compare the output here with the output of your code. So when we run it, if nothing is outputted, that means it passes all the tests. But if—let’s just say—we change this string, we run it, it says Expected
—this is our expected—but we actually got this by running the code.
03:41 So that way, it’s a little easier. Instead of copying and pasting like this, we can just run this command over and over to test our code.
03:50
So now, let’s clean this code up using enumerate()
. enumerate()
is a function that takes in an iterable and iterates through that iterable and returns tuples where the first element of the tuple is the index and the second element is the value.
04:04
So, if we do something like [tup for tup in enumerate([1, 2, 3])]
—forgot to close—we get 0
—which is the index, 1
—which is the value, 1
—which is the index, 2
—which is the value. So, how can we use that to clean this up?
04:21
I’m just going to copy and paste this code below, comment this out, just so you can see the difference, change this to enumerate()
. It takes an iterable, not a number.
04:33
Then, we do num
, like this.
04:39
Save it, close this, run our doctest
module. And it works.
04:45
One nice parameter we can pass into enumerate()
is this extra start
parameter, where we can basically loop through our iterable but the index will actually start at this index.
05:02
So, the code will look like this. And our index is 10
, 11
, 12
. So, that concludes the video regarding range()
and enumerate()
. In the next video, you will learn about list comprehensions and some useful list methods.
James DeHart on June 19, 2020
Mind blown. Thank you.
Abu Shoeb on June 26, 2020
Hi James, I installed iPython and ran it via command line but I don’t see similar interface like your have. Can you please tell me how I can have the similar setup as you? Thanks
James Uejio RP Team on June 27, 2020
Hi @Abu I am also using VSCode in the video with “Dainty – Monokai” theme. I have a split screen setup with iPython + Terminal on one side and code on the other side.
Abu Shoeb on June 30, 2020
I actually realized it after posting the comment :-) Thanks a lot though.
Agil C on July 21, 2020
Hey @James Thanks for the video and mentioning ipython. I didn’t knew about ipython before, so I tried it after watching the view. But when i get into ipython it was surprising the interface was very familiar. I was using it daily for the past couple o months in django shell, didn’t noticed I was using ipython. LOL.
Mark R Baker on July 22, 2020
Hello James, great material! My question is does the order of the if statements matter? I had thought that the if statement for those numbers divisible by 5 and 3 should come first, because it is the strictest. I am a newb, so I apologize if this is a dumb question. Thanks, Mark
James Uejio RP Team on Aug. 4, 2020
@Agil Yea IPython creeps into everything! I just learned about the ipdb
debugger which expands on pdb. import ipdb; ipdb.set_trace()
pypi.org/project/ipdb/
@Mark No dumb questions at all! You are right there is another way to write the code:
if num % 5 == 0 and num % 3 ==0:
numbers[i] = "fizzbuzz"
elif num % 5 == 0:
numbers[i] = "buzz"
elif num % 3 == 0:
numbers[i] = "fizz"
But be careful you need to use if-elif-elif or else the following conditions will override the first one (3 if statements). Try it out yourself!
Mark R Baker on Aug. 13, 2020
Thanks, James!
SatyaRavipati on April 5, 2021
One More version
"""
numbers = [45, 22, 14, 65, 97, 72]
Replace all integers divisible by 3 with "fizz"
Replace all integers divisible by 5 with "buzz"
Replace all integers divisible by 3 and 5 with "fizzbuzz"
>>> my_list_1 = [45, 22, 14, 65, 97, 72]
>>> my_list_2 = [45, 22, 14, 65, 97, 72]
>>> use_range(my_list_1)
Values: ['fizzbuzz', 22, 14, 'buzz', 97, 'fizz']
>>> use_enumerate(my_list_2)
Values: ['fizzbuzz', 22, 14, 'buzz', 97, 'fizz']
"""
from functools import lru_cache
@lru_cache
def is_true_div_by(num, div_num):
val = True if num % div_num == 0 else False
return val
def use_range(my_list: list) -> None:
for idx in range(len(my_list)):
num = my_list[idx]
if is_true_div_by(num, 3) and is_true_div_by(num, 5):
my_list[idx] = "fizzbuzz"
elif is_true_div_by(num, 3):
my_list[idx] = "fizz"
elif is_true_div_by(num, 5):
my_list[idx] = "buzz"
print(f"Values: {my_list}")
def use_enumerate(my_list: list) -> None:
for idx, num in enumerate(my_list):
if is_true_div_by(num, 3) and is_true_div_by(num, 5):
my_list[idx] = "fizzbuzz"
elif is_true_div_by(num, 3):
my_list[idx] = "fizz"
elif is_true_div_by(num, 5):
my_list[idx] = "buzz"
print(f"Values: {my_list}")
dddiiirrr3 on Nov. 2, 2021
You wrote this:
if num % 3 == 0 and num % 5 == 0:
We can rewrite this line like this:
if num % 15 == 0:
If the number should be divisible by 3 and by 5, then it should be divisible by 3 * 5 = 15.
I think it looks more concise. :)
sp4learning19 on July 12, 2024
Awesome.
Vinay Danepalli on Sept. 20, 2024
I believe y’all know it already, but I can say that this logic wouldn’t work if the question asks us to print. Can’t I?
for i in range(len(numbers)):
num = numbers[i]
if num%3 == 0:
print("fizz")
if num%5 == 0:
print("buzz")
if num%3 == 0 and num%5 == 0:
print("fizzbuzz")
Say, number[0] = 15
Output would be
fizz
buzz
fizzbuzz
Become a Member to join the conversation.
James Uejio RP Team on April 27, 2020
I use the interactive Python Terminal iPython. It has color coating and many useful built in methods. You can install it here.